# ruff: noqa: F811 — 임시 층 fixture(world)를 test_m02_labor_ref 에서 받아 씀 """PLAN 806-1 — 구조물 조합 일위대가 보기 = 일위대가 컨테이너 부품 한 벌 · 단가산출 줄 = 그 SK 문서 모달(요구 698). - 화면(소스 지킴) = 조합 상세 · 카드 · 서고 모두 `Labor_Table` 한 부품 · 조합 전용 칸은 `Labor_Combo` · 옛 조합 전용 표 · 단가산출 보기 모달 걷음 · 단가산출 줄 누름 = `Labor_SkEdit`(수식 탭 부품 · 저장 = 그 SK 문서) · 부품 상자 · 700 줄 - 금액(서버) = 같은 문서 · 같은 SK → 같은 금액(전후 같음) · 모달 [저장] 길(`store.write` sangeun) 뒤엔 그 SK 를 가리킨 줄 금액이 따라 바뀜 · 되돌리면 원래 금액 """ import re from decimal import Decimal from pathlib import Path import pytest 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 world # noqa: F401 — 같은 임시 층 ROOT = Path(__file__).resolve().parents[2] M02 = ROOT / "M02_MasterTemplete" PCT = "GC001109" # 원/㎡ · 인력 둘 + 「노무비 * 3 / 100」 COVER = {"덮개": {"labor:LB000036#0": {"qty": 0.5}}} def _src(name: str) -> str: return (M02 / name).read_text("utf-8") def test_조합_상세_카드_서고_한_부품(): detail = _src("M02_MasterTemplete_Labor_Detail.ts") table = _src("M02_MasterTemplete_Labor_Table.ts") assert "return mountLaborTable(host, doc, opt);" in detail # 입구 하나 assert "logicTable" not in detail and "afterRowDrag" not in detail # 옛 조합 전용 표 걷음 assert not ( M02 / "M02_MasterTemplete_Labor_SangeunModal.ts" ).exists() # 옛 단가산출 보기 모달 걷음 assert 'opt.mode === "precombo" ? null : createCombo(' in table # 조합 = 컨테이너 + 덧칸 for text in ("openSkEdit({", "specBar(cur,", "specCells(block, cur,", "dg?.draw("): assert text in table, text assert "수량: fresh()" in table # 새 줄 수량 — 조합 = 면적을 따름 combo = _src("M02_MasterTemplete_Labor_Combo.ts") assert "openQtyModal({" in combo and "linkAreaCell(" in combo and "saveTemplate(" in combo def test_단가산출_줄은_SK_문서_모달_저장은_그_SK(): table = _src("M02_MasterTemplete_Labor_Table.ts") modal = _src("M02_MasterTemplete_Labor_SkEdit.ts") assert 'ref.키.startsWith("SK-")' in modal # SK 참조 줄만 assert "if (isSkRef(row.참조) && !opt.calc)" in table # 셈 주입(프로젝트 복사본)엔 옛 줄 모달 assert "readOnly: !edit || row.참조.층 !== opt.layer," in table # 위 층 SK = 읽기만 assert re.search(r'readTemplate\(layer, "sangeun", opt\.ref\.키, null, opt\.ref\.주인\)', modal) assert re.search( r'saveTemplate\(\s*layer,\s*"sangeun",\s*opt\.ref\.키', modal ) # 저장 = 그 SK 문서 assert ( "mountSangeunDetail(host, doc," in modal and "source: true" in modal ) # 수식 탭 부품 그대로 assert "await handle.flush();" in modal # 수식 저장 먼저 → 문서 저장 assert "if (!opt.readOnly) head.append(ok);" in modal # 읽기만 = [저장] 없음 assert "if (opened.has(at)) return;" in modal # 빨리 두 번 눌러도 하나 assert "closed: (done) => alive && done && recalc()," in table # 저장했으면 금액 다시 셈 def test_부품상자만_700줄_명사형(): for name in ( "M02_MasterTemplete_Labor_Table.ts", "M02_MasterTemplete_Labor_Combo.ts", "M02_MasterTemplete_Labor_SkEdit.ts", "M02_MasterTemplete_Labor_Detail.ts", ): text = _src(name) assert len(text.splitlines()) <= 700, name assert not re.search(r"""\bel\(\s*["'](button|input|select)["']""", text), name assert not re.search(r"""createElement\(\s*["'](button|input|select)["']""", text), name assert "#" not in "".join(re.findall(r"color:\s*[^;]+", text)), name def _total(layer: Path, sk: str, qty: float) -> Decimal: """그 층 일위대가 한 벌(SK 참조 줄 하나 · 수량 qty)의 1 단위 합계.""" with store.at(layer): ref = {"층": "personal", "주인": refs.here()[1], "키": sk} doc = labor.clean({"담은로직": [{"로직": "", "참조": ref, "수량": {"손": qty}}]}) return Decimal(str(labor.calc(doc, {})["반영합"]["계"])) def test_모달_저장_뒤_금액_따라감_되돌리면_같음(world): """모달 [저장] = SK 문서 쓰기(`store.write`) — 그 SK 를 가리킨 일위대가 줄 금액이 바뀌고 · 되돌리면 전후 같음.""" personal = world["personal"] with store.at(personal): key = refs.create( "sangeun", {"이름": "sub1 시험", "단위": "㎡", "바탕": {"로직": PCT}, "더한줄": []} )["이름"] got = store.read("sangeun", key) ver0, doc0 = got["판"], got["문서"] before = _total(personal, key, 2) assert before == _total(personal, key, 2) and before > 0 # 같은 문서 · 같은 SK = 같은 금액 with store.at(personal): cut = {**doc0, **COVER} ver1 = store.write("sangeun", key, ver0, calc.clean_doc(cut, refs.here()))["판"] changed = _total(personal, key, 2) assert changed != before # 품 0.5 로 바뀜 — 일위대가 금액이 그 SK 를 따라감 with store.at(personal): store.write("sangeun", key, ver1, doc0) assert _total(personal, key, 2) == before # 되돌림 = 전후 같음 def test_옛_판_저장_409(world): """모달 [저장]이 낡은 판으로 쓰면 서버가 막음(같은 SK 를 다른 창이 먼저 고침).""" personal = world["personal"] with store.at(personal): key = refs.create( "sangeun", {"이름": "sub1 시험2", "단위": "㎡", "바탕": {"로직": PCT}, "더한줄": []} )["이름"] got = store.read("sangeun", key) store.write("sangeun", key, got["판"], {**got["문서"], "이름": "다른 창이 고침"}) with pytest.raises(store.StoreError) as why: store.write("sangeun", key, got["판"], got["문서"]) assert why.value.status == 409 def test_808_1_모달_머리_일원화(): """808-1(요구 701-4 · 5) — 단가산출 줄 모달 머리 = 페이지 머리(보기 고르기 · [단가 갱신] · [재생성]) · 로직 줄 모달(저장된 일위대가 줄 = 펼친 로직 줄 · 740-1) = 옛 [옵션] · [단가산출로도 저장] · [항목 추가] · [율 줄 추가] 없음 · 보기 고르기 없이 상세만 + 안내(온 단가산출) · 줄 문서 키 SK-000000 = 수식 문서 안 읽음(404 없음).""" sk = _src("M02_MasterTemplete_Labor_SkEdit.ts") assert "slot,\n" in sk and "head.append(slot);" in sk row = _src("M02_MasterTemplete_Labor_RowModal.ts") for gone in ("AlsoSk", "createCheckbox", "openLaborInput", "saveAsSk", "Labor_Option"): assert gone not in row, gone assert "actions: row.참조 ? [slot, ...tools] : tools," in row and 't("FromSk")' in row assert "slot: o.slot," in _src("M02_MasterTemplete_Labor_RowSk.ts") detail = _src("M02_MasterTemplete_Sangeun_Detail.ts") assert "...(opt.slot\n ? []" in detail # 머리 칸 있으면 옛 단추 줄 없음 assert "children: [tabs.picker, tabs.tools]" in detail assert r"/^SK-(?!0{6})\d{6}$/" in detail tabs = _src("M02_MasterTemplete_Sangeun_Tabs.ts") assert 'if (!k && !opt.page && view === "stack")' in tabs