# ruff: noqa: F811 — 임시 층 fixture(world)를 test_m02_labor_ref 에서 받아 씀 """811-1 일위대가 단가산출 줄 = SK 참조 한 행(요구 707-1 · 681 · 698) — 임시 storage · M01 읽기만. - 저장 · 읽기 = 안 펼침(SK 참조 그대로) · 셈 = SK 1 단위 × 수량 · 모달 저장 = 그 SK 문서 → 일위대가 금액 따라감 - [+ 줄] M01 기초 = 그 층 SK 사본(`from-logic` · 같은 내용 = 409 물음 · 씀 · 새로) · 일반 사용자 회사 층 403 - 다른 층 SK = 이 층 사본 뒤 참조(화면 `Labor_SkPick` · 서버 서고 복사 길) - 옮기기 = 로직 줄 → SK + 참조 한 행 · 면적 칸 잇기 옮김 · 전후 금액 같음 · 보기 = 임시 사본 · 실제 저장본 전수 """ import json import re from decimal import Decimal from pathlib import Path import pytest from fastapi import FastAPI from fastapi.testclient import TestClient from common_util.common_util_auth import verify_session from M01_MasterData import M01_MasterData_Store_Combo as combo_store from M02_MasterTemplete import M02_Labor_Doc as labor from M02_MasterTemplete import M02_Labor_Ref as refs from M02_MasterTemplete import M02_Labor_SkRow as skrow from M02_MasterTemplete import M02_Library_Flat as flat from M02_MasterTemplete import M02_MasterTemplete_Store as store from M02_MasterTemplete import M02_Sangeun_Calc as calc from M02_MasterTemplete import M02_Sangeun_Doc as sd from M02_MasterTemplete import M02_Sangeun_Router as sk_router from M02_MasterTemplete import M02_Template_Layers as layers from M02_MasterTemplete.M02_Labor_Expand import Expand from resources.tester.test_740_1_library_flat import USER, _make, _read, _whole from resources.tester.test_m02_labor_ref import MINE, STONE, TEN, world # noqa: F401 ROOT = Path(__file__).resolve().parents[2] M02 = ROOT / "M02_MasterTemplete" FORM = "GC000211" # 합판거푸집 — 재료 줄 있음(일위대가 기본 별도계상) def _src(name: str) -> str: return (M02 / name).read_text("utf-8") def _value(folder, key, given=None): with store.at(folder): got = Expand(_whole(), refs.read_ref, given or {})({**MINE, "키": key}) return got["기준"] def test_참조_한_행_저장_읽기_안_펼침_셈은_SK_곱_수량(world): me = world["personal"] sk = _make(me, "sangeun", 이름="sub1임시 SK", 바탕={"로직": TEN}) sc = _make(me, "precombo", 이름="sub1임시 SC", 담은로직=[{"참조": {**MINE, "키": sk}, "수량": {"손": 3}}]) # fmt: skip doc = _read(me, "precombo", sc) assert doc["담은로직"][0]["참조"] == {**MINE, "키": sk} and "로직" not in doc["담은로직"][0] assert flat.flat_doc("precombo", doc)["담은로직"][0]["참조"]["키"] == sk # 읽기 길도 그대로 one, got = _value(me, sk), _value(me, sc) want = labor.mcut.sums([labor.mcut.row({k: v * 3 for k, v in one.items()})]) assert got["계"] == want["계"] and got["계"] > 0 def test_모달_저장_SK_문서_뒤_일위대가_따라감(world): me = world["personal"] sk = _make(me, "sangeun", 이름="sub1임시 SK", 바탕={"로직": TEN}) sc = _make(me, "precombo", 이름="sub1임시 SC", 담은로직=[{"참조": {**MINE, "키": sk}, "수량": {"손": 1}}]) # fmt: skip before = _value(me, sc)["계"] with store.at(me): # 모달 [저장] = 그 SK 문서 쓰기(`saveTemplate` sangeun) now = store.read("sangeun", sk) cid = sd.line_ids(_whole().logic(TEN))[0] doc = {**now["문서"], "덮개": {cid: {"배율": 2}}} store.write("sangeun", sk, now["판"], calc.clean_doc(doc, refs.here())) assert _value(me, sc)["계"] != before # 일위대가 금액이 SK 를 따라감 assert _read(me, "precombo", sc)["담은로직"][0]["참조"]["키"] == sk # 일위대가 문서는 그대로 def test_from_logic_층_SK_사본_같은것_물음_권한(world): app = FastAPI() app.include_router(sk_router.router) app.dependency_overrides[verify_session] = lambda: USER client = TestClient(app) url = "/api/m02/layers/{}/sangeun/from-logic" res = client.post(url.format("personal"), json={"로직": TEN}) assert res.status_code == 200, res.text key, ref = res.json()["이름"], res.json()["참조"] assert ref == {**MINE, "키": key} doc = _read(world["personal"], "sangeun", key) assert doc["바탕"] == {"로직": TEN} and doc["단위"] == "㎡" # 원/10㎡ → ㎡(줄 ÷ 배와 같은 값) again = client.post(url.format("personal"), json={"로직": TEN}) assert again.status_code == 409 # 같은 내용 = 물음(733-2) use = client.post(url.format("personal"), json={"로직": TEN, "씀": key}) assert use.json()["이름"] == key new = client.post(url.format("personal"), json={"로직": TEN, "새로": True}) assert new.json()["이름"] != key assert client.post(url.format("personal"), json={"로직": "GF999999"}).status_code == 400 assert client.post(url.format("company"), json={"로직": TEN}).status_code == 403 def test_M01_기초_SK_값_로직_줄과_같음_재료_별도계상(world): """새 SK 1 단위 = 옛 로직 줄 ÷ 배 · 재료 빈 칸 = 일위대가 기본(별도계상)을 적어 둠.""" me = world["personal"] for code in (TEN, STONE, FORM): with store.at(me): got = skrow.from_logic(code, new=True) line = labor.clean({"담은로직": [{"로직": code, "수량": {"손": 2}}]}) ref = labor.clean({"담은로직": [{"참조": got["참조"], "수량": {"손": 2}}]}) a, b = labor.calc(line, {}), labor.calc(ref, {}) assert a["반영합"] == b["반영합"], code doc = _read(me, "sangeun", got["이름"]) assert doc["자재표시"] and all(v["별도계상"] == "별도계상" for v in doc["자재표시"].values()) def _labor_world(me): """개인 층 조합 일위대가(수량 빈 줄 = 면적 칸) + 면적 문서 + SC(로직 줄 · 손 수량).""" lines = [{"로직": TEN, "수량": None}, {"로직": STONE, "수량": {"손": 2}}] pill = combo_store.combo_pills({"담은로직": lines})["물량"][0]["키"] with store.at(me): doc = labor.clean({"담은로직": lines}) (me / "labor").mkdir(parents=True, exist_ok=True) (me / "area").mkdir(parents=True, exist_ok=True) (me / "labor" / "c811.json").write_text(json.dumps({**doc, "열": "c811", "단위": "㎡"}, ensure_ascii=False), "utf-8") # fmt: skip area = {"시트": [{"id": "s1", "칸": {"A1": {"값": "4"}}, "연결": [{"키": pill, "칸": "A1"}]}]} (me / "area" / "c811.json").write_text(json.dumps(area, ensure_ascii=False), "utf-8") sc = _make(me, "precombo", 이름="sub1임시 SC", 담은로직=[{"로직": TEN, "수량": {"손": 3}}]) return pill, sc def test_옮기기_로직_줄_SK_참조_면적_잇기_전후_같음(world): me = world["personal"] pill, sc = _labor_world(me) with store.at(me): before = labor.calc(json.loads((me / "labor" / "c811.json").read_bytes()))["반영합"] sc_before = _value(me, sc) rows = {r["키"]: r for r in skrow.run_here()} assert rows["c811"]["같음"] and rows[sc]["같음"] and rows["c811"]["알약"] == 1 doc = json.loads((me / "labor" / "c811.json").read_bytes()) assert all("참조" in x and "로직" not in x for x in doc["담은로직"]) assert [x["수량"] for x in doc["담은로직"]] == [None, {"손": 2}] # 수량 · 별칭 그대로 area = json.loads((me / "area" / "c811.json").read_bytes()) keys = [x["키"] for x in area["시트"][0]["연결"]] assert keys == [refs.ref_id(doc["담은로직"][0]["참조"])] and pill not in keys with store.at(me): assert labor.calc(doc)["반영합"] == before # 원 단위 같음(면적 칸 4) assert _value(me, sc) == sc_before assert refs.refs_in(_read(me, "precombo", sc))[0]["키"].startswith("SK-") made = {k for r in rows.values() for k in r["새SK"]} assert len(made) == 3 # SC TEN · 조합 TEN · STONE — 사본(값 묶음)이 달라 공유 안 함 assert skrow.run_here() == [] # 두 번째 = 옮길 로직 줄 없음 def test_옮기기_다르면_되돌림(world, monkeypatch): me = world["personal"] _labor_world(me) raw = (me / "labor" / "c811.json").read_bytes() real = skrow.money monkeypatch.setattr( skrow, "money", lambda k, key, doc: {"다름": id(doc)} if k == "labor" else real(k, key, doc) ) rows = {r["키"]: r for r in skrow.run_here()} assert not rows["c811"]["같음"] and rows["c811"]["공유없음"] assert (me / "labor" / "c811.json").read_bytes() == raw # 원래대로 left = {p.stem for p in (me / "sangeun").glob("SK-*.json")} sc_made = set(rows[next(k for k in rows if k != "c811")]["새SK"]) assert left == sc_made # 되돌린 문서가 만든 SK 는 지움 def test_보기_임시_사본_공유_storage_안_씀(world, tmp_path): me = world["personal"] _labor_world(me) raw = (me / "labor" / "c811.json").read_bytes() rows = skrow.migrate(False, work=tmp_path) assert rows and all(r["같음"] for r in rows) assert (me / "labor" / "c811.json").read_bytes() == raw # 보기 = 진짜 층 안 고침 assert not (me / "sangeun").exists() or not list((me / "sangeun").glob("SK-*.json")) skrow.to_xlsx(rows, tmp_path / "목록.xlsx") assert (tmp_path / "목록.xlsx").stat().st_size > 0 @pytest.mark.skipif( not any(layers.storage_root().glob("*/*/templates/labor/*.json")), reason="저장본 없음" ) def test_실제_저장본_전수_보기_같음(tmp_path): """공유 storage 층 문서 전부 — 임시 사본에서 옮겨 봄(쓰지 않음) · 줄마다 금액 같음.""" rows = skrow.migrate(False, work=tmp_path) # 이미 옮겼으면 옮길 줄 0 → 통과 assert all(r["같음"] for r in rows), [r["키"] for r in rows if not r["같음"]] def test_마스터_층_단가산출_로직_줄_0(): """옮긴 뒤 상태 — 마스터 층 일위대가 · 조합의 담은로직에 로직 줄 0(참조 줄은 SK · SP · SC).""" for kind in skrow.KINDS: for where, path in layers.layer_files(kind): if where != "마스터": continue lines = [ x for x in json.loads(path.read_bytes()).get("담은로직") or [] if isinstance(x, dict) ] assert not [x for x in lines if x.get("로직")], (kind, path.stem) def test_화면_줄_넣기_SK_사본_참조_부품(): table = _src("M02_MasterTemplete_Labor_Table.ts") pick = _src("M02_MasterTemplete_Labor_SkPick.ts") assert 'opt.layer !== "project" && !opt.calc ? { layer: opt.layer' in table # 프로젝트 = 옛 길 assert "return void skFromLogic(own, x.키).then(" in table # M01 기초 = SK 사본 assert "own && isSkRef(ref)" in table and "void ownSk(own, ref)" in table # 다른 층 = 사본 assert "if (isSkRef(row.참조) && !opt.calc)" in table # 줄 누름 = SK 수식 모달 assert "/sangeun/from-logic" in pick and "{ 본뜬 }" in pick and "withSameAsk(" in pick assert ( "fetchSessionUser()" in pick ) # 주인 null = 자기 층 → 서버 참조 꼴(같은 층 SK 를 사본으로 안 만듦) combo = _src("M02_MasterTemplete_Labor_Combo.ts") assert "...refCards(doc, calc)" in combo and "key: r.참조.키," in combo # 다이어그램 참조 카드 for text in (table, pick): assert len(text.splitlines()) <= 700 assert not re.search(r"""\bel\(\s*["'](button|input|select)["']""", text) assert len((M02 / "M02_Labor_SkRow.py").read_text("utf-8").splitlines()) <= 700 def test_값_한_벌_소수(): assert skrow._unit({"기준": {"계": Decimal("1289832")}, "배": 100}) == "12898.32"