feat(795-2): 일위대가 · 사전조합 로직 사본 — 저장 때 찍음 · [단가 갱신] · [재생성] · 줄 덮개 다시 얹힘
- 새 M02_Labor_Copy — SK 사본(795-1) 한 벌 · 담은로직 로직 줄 · 줄 덮개 더한줄 · 고정 이음 · 자재 MT 가 닿는 M01 닫힌 묶음
- 셈 = Labor_Doc.calc 층 셈 첫머리 사본 덮개 · SC 1 단위 값(Expand) = 그 SC 사본 · 프로젝트 층 · B09 = 옛 길(795-4)
- 사본 정함 = 받은 사본 먼저(열 복사 · 층 복사 = 원본 사본 그대로) → 저장본 → [저장] 때 찍음 · 새 줄만 더함
- 길 POST …/labor/{열 | SC 키}/copy/price|regen · 셈 답 덧칸 사본 표지 · 같은 내용 지문 SC 사본 몫
- Sangeun_Copy 찍기 갈래 나눔(reach · snap · grow) · SK 더한줄 인력 · 재료 · 기계 요소도 사본에
- Labor_Doc 줄 수 그대로(686) · 시험 test_795_2_sc_copy 6 · 기존 일위대가 · SC · SK 시험 통과
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
# ruff: noqa: F811 — 임시 층 fixture(world)를 test_m02_labor_ref 에서 받아 씀
|
||||
"""795-2 일위대가 · 사전조합(SC) 로직 사본 — 임시 storage · M01 은 메모리 덮개(정본 안 고침).
|
||||
|
||||
① 저장 때 사본 · M01 로직 바꿔도 일위대가 · SC 금액 그대로 · 배지 ② 노임 바꿔도 [단가 갱신] 전엔
|
||||
그대로 · 누르면 따라감 ③ [재생성] 뒤 줄 덮개 다시 얹힘 · 못 얹은 칸 알림 ④ 층 복사 · 열 복사 =
|
||||
사본 그대로 ⑤ TS 짝(Labor_Calc.ts settle) 같은 금액 ⑥ 사본 없는 옛 문서 = 옛 길.
|
||||
"""
|
||||
|
||||
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_Copy as lc
|
||||
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_Copy as skc
|
||||
from resources.tester.test_795_1_sk_copy import _changed, _fake, _live
|
||||
from resources.tester.test_m02_labor_calc_mirror import _same, _ts
|
||||
from resources.tester.test_m02_labor_ref import world # noqa: F401 — 같은 임시 층
|
||||
|
||||
PCT = "GC001109" # 원/㎡ · 인력 둘(LB000036 · LB000002) + 「노무비 * 3 / 100」
|
||||
LB = "LB000036"
|
||||
COVER = {"덮개": {f"labor:{LB}#0": {"qty": 0.5}}}
|
||||
COL = "sub2임시"
|
||||
|
||||
|
||||
def _sc(folder, lines):
|
||||
with store.at(folder):
|
||||
body = {"이름": "sub2임시 조합", "단위": "㎡", "담은로직": lines}
|
||||
return refs.create("precombo", body)["이름"]
|
||||
|
||||
|
||||
def _read(folder, kind, key):
|
||||
with store.at(folder):
|
||||
return store.read(kind, key)["문서"]
|
||||
|
||||
|
||||
def _write(folder, kind, key, doc):
|
||||
with store.at(folder):
|
||||
have = (
|
||||
store.read(kind, key)["판"] if (store.root() / kind / f"{key}.json").is_file() else ""
|
||||
)
|
||||
store.write(kind, key, have, doc)
|
||||
|
||||
|
||||
def _labor(folder, lines):
|
||||
_write(folder, "labor", COL, {"열": COL, "담은로직": lines, "자재": []})
|
||||
return _read(folder, "labor", COL)
|
||||
|
||||
|
||||
def _calc(folder, key, doc=None):
|
||||
"""화면 셈 길(`Labor_Router._calc`) — 열 = 일위대가 열 · SC 키."""
|
||||
with store.at(folder):
|
||||
kind = "precombo" if key.startswith("SC-") else "labor"
|
||||
got = labor.clean({**(doc or store.read(kind, key)["문서"]), "열": key})
|
||||
return lc.mark(labor.calc(got, {}), lc.settle(got))
|
||||
|
||||
|
||||
def _unit(folder, key):
|
||||
"""SC 1 단위 값(쓰는 쪽 `Expand` · 안 줄 수량 1)."""
|
||||
ref = {"층": "personal", "주인": "7/42", "키": 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(m01.loaded()[1], given=given)(ref)["기준"]["계"]
|
||||
|
||||
|
||||
def _double(key):
|
||||
row = copy.deepcopy(_live().get(key))
|
||||
row["값"] = row["값"] * 2
|
||||
return row
|
||||
|
||||
|
||||
def test_copy_stamped_logic_change_kept(world, monkeypatch):
|
||||
"""① 일위대가 · SC 저장 때 사본 · M01 로직 바꿔도 금액 그대로 · M01 새 판 배지."""
|
||||
me = world["personal"]
|
||||
sc = _sc(me, [{"로직": PCT, "수량": {"손": 1}}])
|
||||
doc = _labor(me, [{"로직": PCT, "수량": {"손": 2}}])
|
||||
for got in (doc, _read(me, "precombo", sc)):
|
||||
assert PCT in {r["키"] for r in got["사본"]["로직"]}
|
||||
assert LB in {r["키"] for r in got["사본"]["단가"]}
|
||||
was = (_calc(me, COL)["반영합"]["계"], _calc(me, sc)["기준합"]["계"], _unit(me, sc))
|
||||
assert _calc(me, COL)["사본"]["M01새판"] is False
|
||||
_fake(monkeypatch, [_changed(_live().logic(PCT))])
|
||||
assert labor.run_line(m01.loaded()[1], PCT, {})["기준"]["계"] != was[2] # M01 셈은 바뀜
|
||||
now = (_calc(me, COL)["반영합"]["계"], _calc(me, sc)["기준합"]["계"], _unit(me, sc))
|
||||
assert now == was
|
||||
assert _calc(me, COL)["사본"]["M01새판"] is True
|
||||
assert labor.server(_read(me, "labor", COL))["계산값"]["반영합"]["계"] == was[0] # 다시 저장
|
||||
|
||||
|
||||
def test_price_refresh_only_on_press(world, monkeypatch):
|
||||
"""② 노임 바꿈 → 그대로 · [단가 갱신] 미리 셈 = 새로 찍은 문서 값 · [저장] 때 적용."""
|
||||
me = world["personal"]
|
||||
sc = _sc(me, [{"로직": PCT, "수량": {"손": 1}}])
|
||||
was = _calc(me, sc)["기준합"]["계"]
|
||||
_fake(monkeypatch, [_double(LB)])
|
||||
assert _calc(me, sc)["기준합"]["계"] == was and _unit(me, sc) == was
|
||||
fresh = _calc(me, _sc(me, [{"로직": PCT, "수량": {"손": 1}}]))["기준합"]["계"]
|
||||
assert fresh != was
|
||||
with store.at(me):
|
||||
got = lc.preview(labor.clean({**_read(me, "precombo", sc), "열": sc}), lc.PRICE)
|
||||
assert got["전"]["기준합"]["계"] == was and got["후"]["기준합"]["계"] == fresh
|
||||
assert [x["키"] for x in got["바뀐자원"]] == [LB]
|
||||
_write(me, "precombo", sc, {**_read(me, "precombo", sc), lc.JOB: got["doc"][lc.JOB]})
|
||||
saved = _read(me, "precombo", sc)
|
||||
assert lc.JOB not in saved and _unit(me, sc) == fresh
|
||||
assert _calc(me, sc)["사본"]["단가새값"] is False
|
||||
|
||||
|
||||
def test_regen_reapplies_line_cover(world, monkeypatch):
|
||||
"""③ [재생성] — 줄 덮개(cid) 새 사본에 다시 얹힘 · 없어진 줄 덮개 = 못얹은 · 배지 꺼짐."""
|
||||
me = world["personal"]
|
||||
two = {"덮개": {f"labor:{LB}#0": {"qty": 0.5}, "labor:LB000002#0": {"qty": 0.7}}}
|
||||
_labor(me, [{"로직": PCT, "수량": {"손": 1}, "패치": two}])
|
||||
row = copy.deepcopy(_live().logic(PCT))
|
||||
row["호표"] = [x for n, x in enumerate(row["호표"]) if n != 1] # 둘째 줄 없앤 새 판
|
||||
_fake(monkeypatch, [row])
|
||||
assert not _calc(me, COL)["줄"][0].get("짝잃음") # 사본 = 옛 판 그대로
|
||||
with store.at(me):
|
||||
got = lc.preview(labor.clean(_read(me, "labor", COL)), lc.REGEN)
|
||||
assert [x["cid"] for x in got["못얹은"]] == ["labor:LB000002#0"]
|
||||
assert got["전"]["기준합"]["계"] != got["후"]["기준합"]["계"]
|
||||
_write(me, "labor", COL, got["doc"])
|
||||
doc = _read(me, "labor", COL)
|
||||
assert doc["담은로직"][0]["패치"]["덮개"][f"labor:{LB}#0"] == {"qty": 0.5}
|
||||
now = _calc(me, COL)
|
||||
assert now["사본"]["M01새판"] is False and now["기준합"]["계"] == got["후"]["기준합"]["계"]
|
||||
|
||||
|
||||
def test_layer_and_column_copy_keep_copy(world, monkeypatch):
|
||||
"""④ 층 사이 복사(회사 → 개인) · 열 복사 = 원본 사본 그대로(지금 M01 로 다시 안 찍음)."""
|
||||
co, me = world["company"], world["personal"]
|
||||
src = _sc(co, [{"로직": PCT, "수량": {"손": 1}, "패치": COVER}])
|
||||
first = _read(co, "precombo", src)["사본"]
|
||||
old = _labor(me, [{"로직": PCT, "수량": {"손": 2}}])["사본"] # 받는 열 = 옛 M01 사본
|
||||
_fake(monkeypatch, [_double(LB), _changed(_live().logic(PCT))])
|
||||
ref = {"층": "company", "주인": "7", "키": src}
|
||||
with store.at(me):
|
||||
got = refs.create(
|
||||
"precombo", refs.carry(refs.origin_mark(refs.read_ref(ref), ref), ("company", "7"))
|
||||
)
|
||||
mine = _read(me, "precombo", got["이름"])["사본"]
|
||||
assert (mine["판"], mine["단가판"]) == (first["판"], first["단가판"])
|
||||
_write(me, "labor", "sub2임시2", {"열": "sub2임시2", "담은로직": [{"로직": PCT}], "자재": []})
|
||||
other = _read(me, "labor", "sub2임시2")["사본"] # 원본 열 = 바뀐 M01 사본
|
||||
assert other["판"] != old["판"]
|
||||
with store.at(me): # 열 복사(copy_from) = 받은 사본 먼저
|
||||
store.copy_from("labor", COL, "sub2임시2")
|
||||
assert _read(me, "labor", COL)["사본"]["판"] == other["판"]
|
||||
|
||||
|
||||
def test_ts_mirror_same_amount(world, monkeypatch):
|
||||
"""⑤ 사본 셈 답 → TS 짝 settle 같은 답 · M01 바뀐 뒤에도 같은 금액."""
|
||||
me = world["personal"]
|
||||
doc = _labor(me, [{"로직": PCT, "수량": None}])
|
||||
with store.at(me):
|
||||
before = labor.calc(labor.clean(doc), {})
|
||||
_fake(monkeypatch, [_double(LB), _changed(_live().logic(PCT))])
|
||||
with store.at(me):
|
||||
clean = labor.clean(_read(me, "labor", COL))
|
||||
answer = labor.calc(clean, {})
|
||||
assert answer["기준합"] == before["기준합"]
|
||||
key = answer["줄"][0]["알약"]
|
||||
for values in ({}, {key: 3}):
|
||||
_same(labor.settle(answer, clean, values), _ts(answer, clean, values))
|
||||
|
||||
|
||||
def test_old_doc_without_copy_old_path(world, monkeypatch):
|
||||
"""⑥ 사본 없는 옛 일위대가 = 셈은 지금 M01(옛 길) · 다시 저장하면 그때 사본."""
|
||||
me = world["personal"]
|
||||
_labor(me, [{"로직": PCT, "수량": {"손": 1}}])
|
||||
path = me / "labor" / f"{COL}.json"
|
||||
raw = {k: v for k, v in json.loads(path.read_bytes()).items() if k != "사본"}
|
||||
path.write_text(json.dumps(raw, ensure_ascii=False), encoding="utf-8")
|
||||
assert "사본" not in _calc(me, COL)
|
||||
_fake(monkeypatch, [_double(LB)])
|
||||
moved = labor.run_line(m01.loaded()[1], PCT, {})["기준"]["계"]
|
||||
assert _calc(me, COL)["기준합"]["계"] == pytest.approx(moved, abs=1)
|
||||
with store.at(me):
|
||||
assert skc.has(labor.server(raw))
|
||||
Reference in New Issue
Block a user