Files
Aislo/resources/tester/test_795_1_sk_copy.py
T
eomsangdonandClaude Opus 5.5 5112bc1425 feat(795-1): SK 로직 사본 — 저장 때 M01 닫힌 묶음 찍음 · [단가 갱신] · [재생성] · M01 새 판 배지
- 새 M02_Sangeun_Copy — 사본 = 로직 · 하위 로직 · 찾기 표 · 그 밖 요소(로직 갈래) + 자원 단가 LB · MT · EQ · MO · FX(단가 기준 시점 갈래)
- 셈 = M01 색인 위 사본 줄 덮개(Copy) · Calc 바탕 줄 · run · 저장 · 셈 길 첫머리만 고리(줄 수 그대로)
- 사본은 서버가 정함(저장본 사본 · 없으면 받은 사본 · 그것도 없으면 저장 때 찍음) · 할일 칸 사본할일 = [저장] 때 적용 · 자동 갱신 없음
- 지워진 M01 키 = 사본 마지막 값 · 표지 지운키 · 사본 없는 옛 SK = 옛 길
- 같은 내용 지문에 사본 몫(지금 M01 과 다른 판만) · 길 POST …/sangeun/copy/price|regen
- 시험 test_795_1_sk_copy 6 · 기존 SK · 지문 시험 294 통과

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 11:16:39 +09:00

214 lines
8.9 KiB
Python

# ruff: noqa: F811 — 임시 층 fixture(world)를 test_m02_labor_ref 에서 받아 씀
"""795-1 SK 로직 사본 — 저장 때 M01 닫힌 묶음을 찍고 셈은 사본으로(임시 storage · M01 은 메모리 덮개).
① M01 로직 줄 바꿔도 사본 SK 금액 그대로 ② 노임 바꿔도 [단가 갱신] 전엔 그대로 · 누르면 따라감
③ [재생성] 뒤 고친 칸 다시 얹힘 · 못 얹은 칸 알림 ④ M01 새 판 배지 ⑤ 지워진 키 = 마지막 값
⑥ 옛 SK(사본 없음) = 옛 길 · 지문 그대로 ⑦ 가져온 식 SK 같은 규칙.
"""
from __future__ import annotations
import copy
import json
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_Library_Same as same
from M02_MasterTemplete import M02_MasterTemplete_Store as store
from M02_MasterTemplete import M02_Sangeun_Calc as calc
from M02_MasterTemplete import M02_Sangeun_Copy as skc
from M02_MasterTemplete import M02_Sangeun_Imported as imp
from resources.tester.test_m02_labor_ref import world # noqa: F401 — 같은 임시 층
PCT = "GC001109" # 원/㎡ · 인력 둘 + 「노무비 * 3 / 100」
MAN = "LB000002" # 보통인부
def _live():
return m01.loaded()[1]
def _fake(monkeypatch, rows=(), drop=()):
"""M01 바꿈 흉내 — 지금 색인 위에 바뀐 줄 · 지운 키(정본 파일 안 고침)."""
files, live = m01.loaded()
fake = skc.Copy(live, [copy.deepcopy(r) for r in rows])
for key in drop:
skc.Copy._own(fake.index, live.index, key[:2]).pop(key, None)
monkeypatch.setattr(m01, "loaded", lambda: (files, fake))
return fake
def _sk(folder, **doc):
with store.at(folder):
body = {"이름": "sub2임시 사본", "단위": "㎡", "더한줄": [], **doc}
return refs.create("sangeun", body)["이름"]
def _doc(folder, key):
with store.at(folder):
return store.read("sangeun", key)["문서"]
def _save(folder, key, doc):
with store.at(folder):
store.write("sangeun", key, store.read("sangeun", key)["판"], doc)
def _calc(folder, doc):
with store.at(folder):
return calc.calc_doc(doc, None, False)
def _sum(folder, key):
return _calc(folder, _doc(folder, key))["기준"]["계"]
def _cids(folder, key):
return [x["cid"] for x in _calc(folder, _doc(folder, key))["줄"]]
def _changed(row, at=0):
row = copy.deepcopy(row)
row["호표"][at]["수량"] = f"({row['호표'][at]['수량']}) * 2"
return row
def test_copy_stamped_and_logic_change_kept(world, monkeypatch):
"""① · ④ 저장 때 사본(로직 · 단가 갈래) · M01 로직 바꿔도 금액 그대로 · 배지만."""
me = world["personal"]
key = _sk(me, 바탕={"로직": PCT})
got = _doc(me, key)["사본"]
assert PCT in {r["키"] for r in got["로직"]}
assert got["단가"] and all(r["키"][:2] in skc.RES for r in got["단가"])
assert not any(r["키"][:2] in skc.RES for r in got["로직"])
first = _calc(me, _doc(me, key))
assert first["사본"]["M01새판"] is False and first["사본"]["지운키"] == []
was = first["기준"]["계"]
plain = labor.run_line(_live(), PCT, {})["기준"]["계"]
_fake(monkeypatch, [_changed(_live().logic(PCT))])
assert labor.run_line(m01.loaded()[1], PCT, {})["기준"]["계"] != plain # M01 셈은 바뀜
now = _calc(me, _doc(me, key))
assert now["기준"]["계"] == was
assert now["사본"]["M01새판"] is True
def test_price_refresh_only_on_press(world, monkeypatch):
"""② 노임 바꿈 → 그대로 · [단가 갱신] 미리 셈 · 저장 = 새 SK 와 같은 값 · 바뀐자원."""
me = world["personal"]
key = _sk(me, 바탕={"로직": PCT})
was = _sum(me, key)
lb = _cids(me, key)[0].split(":")[1].split("#")[0]
row = copy.deepcopy(_live().get(lb))
row["값"] = row["값"] * 2
_fake(monkeypatch, [row])
assert _sum(me, key) == was # 누르기 전 = 단가 기준 시점 값
fresh = _sum(me, _sk(me, 바탕={"로직": PCT})) # 지금 M01 로 새로 찍은 SK
assert fresh != was
with store.at(me):
got = skc.preview(_doc(me, key), skc.PRICE)
assert got["전"]["계"] == was and got["후"]["계"] == fresh
assert [x["키"] for x in got["바뀐자원"]] == [lb]
assert got["문서"][skc.JOB] == skc.PRICE
_save(me, key, got["문서"]) # [저장] = 서버가 할일 적용
saved = _doc(me, key)
assert skc.JOB not in saved and _sum(me, key) == fresh
assert saved["사본"]["단가시점"] >= saved["사본"]["시점"]
def test_regen_reapplies_covers(world, monkeypatch):
"""③ [재생성] — 덮개 · 까닭 새 사본에 다시 얹힘 · 없어진 줄 덮개 = 못얹은 · 배지 꺼짐."""
me = world["personal"]
probe = _sk(me, 바탕={"로직": PCT})
one, two = _cids(me, probe)[:2]
covers = {one: {"qty": 0.5}, two: {"qty": 0.7}}
key = _sk(me, 바탕={"로직": PCT}, 덮개=covers, 까닭={one: "현장 실측"})
row = copy.deepcopy(_live().logic(PCT))
row["호표"] = [x for n, x in enumerate(row["호표"]) if n != 1] # 둘째 줄 없앤 새 판
_fake(monkeypatch, [row])
with store.at(me):
got = skc.preview(_doc(me, key), skc.REGEN)
assert [x["cid"] for x in got["못얹은"]] == [two]
assert got["전"]["계"] != got["후"]["계"]
_save(me, key, got["문서"])
doc = _doc(me, key)
assert doc["덮개"][one]["qty"] == 0.5 and doc["까닭"][one] == "현장 실측"
now = _calc(me, doc)
assert now["줄"][0]["덮개"] == ["qty"]
assert now["사본"]["M01새판"] is False
assert now["기준"]["계"] == got["후"]["계"]
def test_deleted_keys_keep_last_value(world, monkeypatch):
"""⑤ 사본이 부르는 M01 키(바탕 로직 · 노임)가 지워져도 마지막 값 · 지운키 표지."""
me = world["personal"]
key = _sk(me, 바탕={"로직": PCT})
was = _sum(me, key)
lb = _cids(me, key)[0].split(":")[1].split("#")[0]
_fake(monkeypatch, drop=[PCT, lb])
with pytest.raises(Exception):
m01.loaded()[1].logic(PCT)
now = _calc(me, _doc(me, key))
assert now["기준"]["계"] == was
assert {PCT, lb} <= set(now["사본"]["지운키"])
with store.at(me):
got = skc.preview(_doc(me, key), skc.PRICE)
assert {"키": lb, "이름": got["바뀐자원"][0]["이름"], "지워짐": True} in got["바뀐자원"]
assert got["후"]["계"] == was
def test_old_sk_without_copy_old_path(world, monkeypatch):
"""⑥ 사본 없는 옛 SK = M01 지금 값(옛 길) · 같은 내용 지문 = 새 사본 SK 와 같음."""
me = world["personal"]
key = _sk(me, 바탕={"로직": PCT})
path = me / "sangeun" / f"{key}.json"
raw = json.loads(path.read_bytes())
old = {k: v for k, v in raw.items() if k != "사본"}
path.write_text(json.dumps(old, ensure_ascii=False), encoding="utf-8")
fresh = _sk(me, 바탕={"로직": PCT})
assert _sum(me, key) == _sum(me, fresh)
assert "사본" not in _calc(me, _doc(me, key))
with store.at(me):
assert same.Same().finger("sangeun", old) == same.Same().finger("sangeun", _doc(me, fresh))
_fake(monkeypatch, [_changed(_live().logic(PCT))])
moved = labor.run_line(m01.loaded()[1], PCT, {})["기준"]["계"]
assert _sum(me, key) == pytest.approx(moved, abs=1) # 옛 길 = 따라감
assert _sum(me, fresh) != _sum(me, key) # 사본 SK = 그대로
with store.at(me): # 판 다른 두 SK = 지문 다름(오판 막음)
assert same.Same().finger("sangeun", _doc(me, key)) != same.Same().finger(
"sangeun", _doc(me, fresh)
)
def _imported_row():
man = {"종류": "인력", "이름": "보통인부", "단위": "인", "수량": "0.5", "비목": "노무비"}
mat = {"종류": "재료", "이름": "각재", "단위": "㎥", "수량": "0.01", "비목": "재료비"}
return {
"이름": "sub2임시 가져온",
"결과단위": "원/㎡",
"호표": [{**man, "단가": 165545, "이음": MAN}, {**mat, "단가": 500661}],
}
def test_imported_same_rule(world, monkeypatch):
"""⑦ 가져온 식 SK(단가기준 우리) — 이음 노임 바꿔도 그대로 · [단가 갱신] 뒤 따라감."""
me = world["personal"]
with store.at(me):
key = imp.create(_imported_row(), None, "sub2임시 가져온")["이름"]
doc = _doc(me, key)
assert MAN in {r["키"] for r in doc["사본"]["단가"]}
with store.at(me):
ours = imp.to_price(doc, "우리")["문서"]
_save(me, key, ours)
was = _sum(me, key)
row = copy.deepcopy(_live().get(MAN))
row["값"] = row["값"] * 2
_fake(monkeypatch, [row])
assert _sum(me, key) == was
with store.at(me):
got = skc.preview(_doc(me, key), skc.PRICE)
assert [x["키"] for x in got["바뀐자원"]] == [MAN]
assert got["후"]["노무비"] == pytest.approx(got["전"]["노무비"] * 2, abs=1)