# ruff: noqa: F811 — 임시 층 fixture(world)를 test_m02_labor_ref 에서 받아 씀 """822-1 사본 빈틈 막기(요구 797-1) — 길마다: 만들기 → [저장] → M01 값(임시 · 메모리 덮개)을 기계 가격 · 노임 · 유가 · 자재 값으로 바꿈 → 금액 그대로 + 「단가새값」 → [단가 갱신] → 반영. 길 = 수식 길 줄 쌓기(품셈 기계 · 바꿔 끼운 기계 · 자원 줄 · 단가산출 참조) · 옛 로직 길(더한줄 로직 · 재료 · 인력) · 가져온 식(단가기준 우리) · 층 복사 · [재생성] · 빈틈 SK 표시(`사본없음`). M01 바꿈 = `skc.Copy` 아닌 진짜 색인 사본(`dc.base` 가 못 벗김) · 정본 파일 안 고침. """ from __future__ import annotations import copy import json import pytest from M01_MasterData import M01_MasterData_Store as m01 from M02_MasterTemplete import M02_Doc_Sk_Path as path from M02_MasterTemplete import M02_Labor_Ref as refs from M02_MasterTemplete import M02_MasterTemplete_Store as store from M02_MasterTemplete import M02_Sangeun_Copy as skc from resources.tester.test_m02_labor_ref import world # noqa: F401 — 같은 임시 층 DIG, DIG_NO, DIG_LOGIC = "EQ000022", "0201-0100", "GC000267" # 굴착기(무한궤도) 1.0㎥ DIG2_NO = "0201-0070" # 바꿔 끼울 굴착기 MAN = "LB000002" # 보통인부 PCT = "GC001109" # 금속기와 해체(인력 둘 + 노무비 3 %) def _live(): return m01.loaded()[1] def _move(monkeypatch, *keys: str) -> None: """M01 값 두 배 — 키마다 값 칸(기계 = 가격 · 자재 = 물가 칸) · 진짜 색인 사본(Copy 아님) · 수식 문서 폴더 그대로.""" files, live = m01.loaded() fake = copy.copy(live) fake.index, fake.numbers = dict(live.index), dict(live.numbers) for key in keys: tid = key[:2] row = copy.deepcopy(live.index[tid][key]) if tid == "EQ": row["값"]["가격"] *= 2 elif tid == "MT": for k in ("물가자료", "유통물가", "물가정보", "거래가격"): row[k] = row[k] * 2 if row.get(k) else row.get(k) else: row["값"] *= 2 fake.index[tid] = {**fake.index[tid], key: row} if row.get("원문번호"): fake.numbers[tid] = {**fake.numbers[tid], str(row["원문번호"]): row} monkeypatch.setattr(m01, "loaded", lambda: (files, fake)) def _at(folder, fn, *args): with store.at(folder): return fn(*args) def _sk(folder, **body) -> str: body = {"이름": "sub1임시 빈틈", "단위": "시간", "더한줄": [], **body} return _at(folder, lambda: refs.create("sangeun", body)["이름"]) def _var(sym: str, link: dict, name: str = "") -> dict: return {"기호": "P", "아래": sym, "위": "", "이름": name or sym, "단위": "", "연결": link} def _line(sym: str, part: str = "경비") -> dict: return {"이름": sym, "단위": "hr", "비목": part, "수량": "1", "단가": f"P_{sym}"} def _stack(folder, key, rows: list[tuple[str, dict, str]]) -> None: """줄 쌓기 — 지금 문서에 (기호, 연결, 비목) 줄을 더해 수식 길로 저장.""" got = _at(folder, path.read, key) doc = copy.deepcopy(got["문서"]) doc.setdefault("변수", []).extend(_var(s, link) for s, link, _ in rows) doc.setdefault("금액", []).extend(_line(s, part) for s, _, part in rows) _at(folder, path.save, key, {"문서": doc, "판": got["판"]}) def _sum(folder, key): return _at(folder, path.read, key)["셈"]["계"] def _price(folder, key) -> dict: """[단가 갱신] 저장 — 바뀐 자원 키 · 뒤 금액.""" look = _at(folder, path.preview, key, {"할일": skc.PRICE}) 판 = _at(folder, path.read, key)["판"] _at(folder, path.save, key, {"할일": skc.PRICE, "판": 판}) return {"바뀐": sorted(x["키"] for x in look["바뀐자원"]), "후": _sum(folder, key)} MACHINE = {"갈래": "단가산출", "키": DIG_LOGIC, "입력": {"기계": f"'{DIG_NO}'"}} def _fuel_and_crew(no: str = DIG_NO) -> tuple[str, str]: """기계 시간당 식이 읽는 유가(MO) · 조종원 노임(LB) 키.""" w = _live() row = w.numbers["EQ"][no] fuel = w.numbers["MO"][f"{row['값']['주연료종류']}:전국평균"]["키"] crew = w.numbers["LB"][str(row["값"]["조종원직종"])]["키"] return fuel, crew # ── 수식 길 줄 쌓기 ─────────────────────────────────────────────────── @pytest.mark.parametrize("what", ["가격", "유가", "노임"]) def test_stack_m01_machine(world, monkeypatch, what): """품셈 기계(굴착기 1.0㎥) 줄 쌓기 → 저장 → 기계 가격 · 유가 · 조종원 노임 바뀜 → 금액 그대로 + 단가새값 → [단가 갱신] → 반영.""" me = world["personal"] key = _sk(me) _stack(me, key, [("기계", MACHINE, "묶음")]) was = _sum(me, key) assert was > 0 fuel, crew = _fuel_and_crew() moved = {"가격": DIG, "유가": fuel, "노임": crew}[what] _move(monkeypatch, moved) now = _at(me, path.read, key) assert now["셈"]["계"] == was and now["사본"]["단가새값"] is True got = _price(me, key) assert got["바뀐"] == [moved] and got["후"] > was def test_stack_swapped_machine(world, monkeypatch): """바꿔 끼운 기계 — 기계 입력을 다른 굴착기로 바꿔 저장 → 그 기계 값이 사본에.""" me = world["personal"] key = _sk(me) _stack(me, key, [("기계", MACHINE, "묶음")]) got = _at(me, path.read, key) doc = copy.deepcopy(got["문서"]) for v in doc["변수"]: if (v.get("연결") or {}).get("키") == DIG_LOGIC: v["연결"]["입력"] = {"기계": f"'{DIG2_NO}'"} _at(me, path.save, key, {"문서": doc, "판": got["판"]}) was = _sum(me, key) other = _live().numbers["EQ"][DIG2_NO]["키"] _move(monkeypatch, other) assert _sum(me, key) == was assert _price(me, key)["바뀐"] == [other] @pytest.mark.parametrize("kind", ["인력", "재료"]) def test_stack_resource(world, monkeypatch, kind): """자원 줄 더하기(노임 · 자재 요소) — 저장 뒤 값 바뀌어도 그대로 · 갱신 뒤 반영.""" me = world["personal"] res = MAN if kind == "인력" else next(iter(_live().index["MT"])) key = _sk(me) part = "노무비" if kind == "인력" else "재료비" _stack(me, key, [("자원", {"갈래": "요소", "키": res}, part)]) was = _sum(me, key) assert was > 0 _move(monkeypatch, res) assert _sum(me, key) == was assert _price(me, key)["바뀐"] == [res] def test_stack_logic_ref(world, monkeypatch): """단가산출 참조(M01 단가산출을 부름) — 그 식이 읽는 노임도 사본에.""" me = world["personal"] key = _sk(me) _stack(me, key, [("참조", {"갈래": "단가산출", "키": PCT, "입력": {}}, "묶음")]) was = _sum(me, key) assert was > 0 _move(monkeypatch, MAN) assert _sum(me, key) == was assert MAN in _price(me, key)["바뀐"] # ── 옛 로직 길(더한줄 · 파생) ──────────────────────────────────────── OLD_ADDS = { "로직 기계": ([{"종류": "로직", "로직": DIG_LOGIC, "입력값": {"기계": DIG_NO}, "수량": 1}], {"기계": DIG_NO}, DIG), "재료 요소": ([{"종류": "재료", "요소": "MT000008", "수량": 1}], {}, "MT000008"), "인력 요소": ([{"종류": "인력", "요소": MAN, "수량": 1}], {}, MAN), } # fmt: skip @pytest.mark.parametrize("name", list(OLD_ADDS)) def test_old_added_lines(world, monkeypatch, name): """옛 로직 길 더한줄(로직 기계 · 재료 요소 · 인력 요소) — 저장 뒤 그대로 · 갱신 뒤 반영.""" me = world["personal"] adds, given, moved = OLD_ADDS[name] key = _sk(me, 더한줄=adds, 입력값=given) was = _sum(me, key) assert was > 0 assert _at(me, path.read, key)["사본"]["사본없음"] == 0 _move(monkeypatch, moved) assert _sum(me, key) == was got = _price(me, key) assert moved in got["바뀐"] and got["후"] > was # ── 가져온 식 · 층 복사 · 재생성 ────────────────────────────────────── def test_imported_ours(world, monkeypatch): """가져온 식 단가기준 우리 — 이음 노임 바뀌어도 그대로 · 갱신 뒤 반영 · 엑셀 기준 = 엑셀 값.""" from M02_MasterTemplete import M02_Sangeun_Imported as imp me = world["personal"] man = {"종류": "인력", "이름": "보통인부", "단위": "인", "수량": "0.5", "비목": "노무비"} row = { "이름": "sub1임시 가져온", "결과단위": "원/㎡", "호표": [{**man, "단가": 165545, "이음": MAN}], } key = _at(me, lambda: imp.create(row, None, "sub1임시 가져온")["이름"]) assert _sum(me, key) == 82772 # 엑셀 단가 그대로 doc = _at(me, lambda: store.read("sangeun", key)["문서"]) ours = _at(me, lambda: imp.to_price(doc, "우리")["문서"]) _at(me, lambda: store.write("sangeun", key, store.read("sangeun", key)["판"], ours)) was = _sum(me, key) _move(monkeypatch, MAN) assert _sum(me, key) == was doc = _at(me, lambda: store.read("sangeun", key)["문서"]) got = _at(me, skc.preview, doc, skc.PRICE) assert [x["키"] for x in got["바뀐자원"]] == [MAN] and got["후"]["계"] > was def test_layer_copy_keeps_copy(world, monkeypatch): """층 복사(개인 → 회사) — 줄 쌓기 기계 사본째 · 원본 M01 값 바뀐 뒤 복사해도 그 값.""" me, co = world["personal"], world["company"] key = _sk(me) _stack(me, key, [("기계", MACHINE, "묶음")]) was = _sum(me, key) _move(monkeypatch, DIG) ref = {"층": "personal", "주인": "7/42", "키": key} with store.at(co): doc = refs.read_ref(ref) new = refs.create("sangeun", refs.carry(refs.origin_mark(doc, ref), ("personal", "7/42"))) assert _sum(co, new["이름"]) == was assert _at(co, path.read, new["이름"])["사본"]["단가새값"] is True def test_regen_follows_m01(world, monkeypatch): """[재생성] — 지금 M01 로 다시 찍음(따로 문서 버림) · 그 뒤 M01 바뀌어도 그대로.""" me = world["personal"] key = _sk(me, 바탕={"로직": PCT}, 단위="㎡") _stack(me, key, [("기계", MACHINE, "묶음")]) _move(monkeypatch, MAN) 판 = _at(me, path.read, key)["판"] _at(me, path.save, key, {"할일": skc.REGEN, "판": 판}) after = _at(me, path.read, key) assert after["사본"]["따로"] is False and after["사본"]["사본없음"] == 0 assert not after["사본"]["단가새값"] was = after["셈"]["계"] _move(monkeypatch, MAN) # 한 번 더(네 배) assert _sum(me, key) == was def test_gap_mark_then_save_fills(world, monkeypatch): """빈틈 있는 옛 SK — 열면 지금 값으로 셈 + 「사본없음 n」 · 일괄 고쳐 쓰지 않음 · [저장] 하면 지금 값으로 굳음(n = 0) · 그 뒤 M01 바뀌어도 그대로.""" me = world["personal"] key = _sk(me) _stack(me, key, [("기계", MACHINE, "묶음")]) file = me / "sangeun" / f"{key}.json" raw = json.loads(file.read_text(encoding="utf-8")) vals = raw["문서"]["수식"] if "문서" in raw else raw["수식"] for part in ("로직", "단가"): vals[part] = [] # 옛 저장본 흉내 — 줄 쌓기 기계를 사본에 안 박은 때 file.write_text(json.dumps(raw, ensure_ascii=False), encoding="utf-8") before = file.read_bytes() got = _at(me, path.read, key) assert got["사본"]["사본없음"] > 0 was = got["셈"]["계"] assert file.read_bytes() == before # 열기 = 안 고쳐 씀 files, live = m01.loaded() _move(monkeypatch, DIG) moved = _sum(me, key) assert moved > was # 빈틈 = 지금 값으로 셈 got = _at(me, path.read, key) _at(me, path.save, key, {"문서": got["문서"], "판": got["판"]}) now = _at(me, path.read, key) assert now["사본"]["사본없음"] == 0 and now["셈"]["계"] == moved monkeypatch.setattr(m01, "loaded", lambda: (files, live)) # 앞 값(절반) assert _sum(me, key) == moved