Files
Aislo/resources/tester/test_715_3_labor_patch.py
T
eomsangdonandClaude Opus 5.5 7d007e4feb feat(723-1): 일위대가(사전조합) 안 줄 수량 저장 안 함 — 구조물 조합 안 줄 알약 연결 · 불러오기 수량 = 본뜬 출처 기록
- 저장(`M02_Labor_Ref.no_qty`) = 안 줄 · 자재 수량 늘 null(비율 고정 줄 율만) · 기본 {손: 1} 걷음 · 굳힌 사본도 걷음
- 펼침(`Expand`) = 안 줄 수량 = 쓰는 쪽 given 알약 「참조|층|주인|키#차례」 · 「…#자재|코드」 · 층 원본 옛 손 안 씀 ·
  굳힌 사본(프로젝트) 손 · 비율 줄 율만 · 없으면 「수량 없음」(1 아님 · 그 일위대가 기준 없음)
- 구조물 조합 알약(`pills`) = 참조 줄 + 안 줄 알약(`inner_pills`) — 면적산출 칸에 이음
- 불러오기(`Hopyo_Build`) = 수량 안 들임 · 엑셀 수량 = `본뜬.실무수량`(셈 안 씀 · 엑셀 대조만 · 하위 호표까지)
- B08 `_ref_mats` = 안 줄 수량 = 굳힌 손 · 아니면 사본 면적 알약 값
- 낡은 시험 고침(사유 = 수량 저장 안 함 · 이은 값으로 셈): test_m02_labor_ref · test_175_precombo_mats ·
  test_715_3_labor_patch · test_m02_hopyo_build · test_m02_hopyo_flow · test_160_3(굳힌 옛 사본 꼴 손 채움)
- 남김: B09 시험 5(test_204_2 · test_703 · test_719_1 · test_719_2) = sub8 723-4 가 B09 코드와 함께

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 21:51:27 +09:00

140 lines
6.4 KiB
Python

# 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):
"""안 줄 수량 = 모두 1(723-1 · 저장 안 함 → 쓰는 쪽 알약 값)."""
ref = {**MINE, "키": key}
with store.at(folder):
lines = refs.read_ref(ref)["담은로직"]
given = {f"{refs.ref_id(ref)}#{i}": 1 for i in range(len(lines))}
return refs.Expand(_whole(), given=given)(ref)
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, {})["기준"]["계"]
)