# ruff: noqa: F811 — 임시 층 fixture(world)를 test_m02_labor_ref 에서 받아 씀 """일위대가 · 사전조합 줄 덮개 · [단가산출로 저장] · 단위 빈칸 막기(PLAN 715-3) — 임시 storage. 줄 덮개 = 그 줄에만(단가산출 원본 그대로) · 값 = 같은 덮개 SK 값 = 손셈 · 줄 고름(차례)이 뺀줄 뒤 제자리 · [단가산출로 저장] = 새 SK + 참조 줄 · 같은 값 · 단위 빈칸 400 · 덮개 없는 줄 = 옛 값. """ 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" # 원/㎡ · 인력 둘(LB000036 · LB000002) + 「노무비 * 3 / 100」 TWO_MATS = "GC000565" # 원/m · 재료 줄 둘 COVER = {"덮개": {"labor:LB000036#0": {"qty": 0.5}}} def _whole(): return m01.loaded()[1] def _sc(folder, lines, unit="㎡"): with store.at(folder): return refs.create("precombo", {"이름": "조합", "단위": unit, "담은로직": lines})["이름"] def _value(folder, key): with store.at(folder): return refs.Expand(_whole())({**MINE, "키": key}) def test_logic_line_cover_in_precombo(world): """사전조합 로직 줄 덮개 = 같은 덮개 SK 값 · 손셈(품 0.5 × 단가 + 둘째 줄 · 3 % 덧줄).""" bare = labor.run_line(_whole(), PCT, {}) sc = _sc(world["personal"], [{"로직": PCT, "패치": COVER}]) with store.at(world["personal"]): kept = store.read("precombo", sc)["문서"]["담은로직"][0] assert kept["패치"] == COVER got = _value(world["personal"], sc)["기준"] labor_sum = Decimal("0.5") * Decimal(str(bare["줄"][0]["단가"])) + Decimal( str(bare["줄"][1]["금액"]) ) assert got["노무비"] == pytest.approx(labor_sum) assert got["경비"] == pytest.approx(labor_sum * 3 / 100, abs=1) def test_line_pick_by_order_stays_after_drop(world): """줄 고름 = 바탕 호표 차례(2) · 패치 뺀줄로 첫 재료 줄을 빼도 그 자재가 그 줄에.""" import master_pick as mp whole = _whole() row = whole.logic(TWO_MATS) item = row["호표"][2] want = mp.search(whole, item["요소"], {}, item.get("단위"))[1] one = { "로직": TWO_MATS, "고름": {"2": want}, "자재표시": {"2": {"별도계상": "직접계산"}}, "패치": {"뺀줄": ["material:" + row["호표"][1]["이름"] + "#0"]}, } with store.at(world["personal"]): doc = labor.clean({"담은로직": [one]}) got = labor.calc(doc, {})["줄"][0] picked = [x for x in got["줄"] if x.get("품목")] assert [x["품목"]["키"] for x in picked] == [want] def test_ref_line_cover_only_that_line(world): """SK 참조 줄 덮개 = 그 줄만 · SK 원본 · 다른 줄 값 그대로.""" with store.at(world["personal"]): sk = refs.create( "sangeun", {"이름": "밑", "단위": "㎡", "바탕": {"로직": PCT}, "더한줄": []} )["이름"] before = store.read("sangeun", sk)["판"] ref = {**MINE, "키": sk} sc = _sc( world["personal"], [{"참조": ref, "수량": {"손": 1}, "패치": COVER}, {"로직": "GC001111", "수량": {"손": 1}}], ) inner = _value(world["personal"], sc)["안"] plain = _value(world["personal"], _sc(world["personal"], [{"참조": ref}]))["기준"] covered = calc.calc_doc( {"키": "SK-000999", "이름": "x", "단위": "㎡", "바탕": {"로직": PCT}, **COVER} ) assert inner[0]["기준"]["계"] == pytest.approx(Decimal(str(covered["1단위"]["계"]))) assert inner[0]["기준"]["계"] != plain["계"] with store.at(world["personal"]): assert store.read("sangeun", sk)["판"] == before def test_save_as_sangeun(world): """[단가산출로 저장] — 새 SK(바탕 · 입력값 · 고름 cid · 덮개) · 참조 줄 · 같은 1 단위 값.""" one = {"로직": PCT, "패치": COVER, "수량": {"손": 2}, "별칭": "내 줄"} with store.at(world["personal"]): line_value = labor.calc(labor.clean({"담은로직": [one]}), {})["줄"][0]["기준"] got = calc.from_line(one, "줄로 만든", "㎡") doc = store.read("sangeun", got["이름"])["문서"] value = calc.calc_doc(doc)["기준"] assert got["줄"] == {"참조": {**MINE, "키": got["이름"]}, "수량": {"손": 2}, "별칭": "내 줄"} assert doc["바탕"] == {"로직": PCT} and doc["덮개"]["labor:LB000036#0"]["qty"] == 0.5 assert value["계"] == pytest.approx(line_value["계"]) def test_refusals(world): with store.at(world["personal"]): with pytest.raises(store.StoreError, match="단위가 빔"): refs.create("precombo", {"이름": "x", "단위": " ", "담은로직": [{"로직": PCT}]}) assert refs.create("precombo", {"이름": "빈 새 문서", "단위": None, "담은로직": []})["이름"] bad = {"로직": PCT, "패치": {"더한줄": [{"참조": {**MINE, "키": "SK-000001"}, "수량": 1}]}} with pytest.raises(store.StoreError, match="참조 못 넣음"): labor.clean({"담은로직": [bad]}) with pytest.raises(store.StoreError, match="수가 아님"): labor.clean( {"담은로직": [{"로직": PCT, "패치": {"덮개": {"labor:LB000036#0": {"qty": "식"}}}}]} ) sc = _sc(world["personal"], [{"로직": PCT}]) with pytest.raises(store.StoreError, match="줄 덮개는"): labor.clean({"담은로직": [{"참조": {**MINE, "키": sc}, "패치": COVER}]}) def test_no_patch_same_value(world): """덮개 없는 줄 = run_line 값 그대로(옛 셈 길).""" sc = _sc(world["personal"], [{"로직": PCT, "패치": {}}]) with store.at(world["personal"]): assert "패치" not in store.read("precombo", sc)["문서"]["담은로직"][0] assert _value(world["personal"], sc)["기준"]["계"] == pytest.approx( labor.run_line(_whole(), PCT, {})["기준"]["계"] )