- 문서 칸 `수식`(옛 `사본` 은 802-5 까지 옆에) = 값 사본(로직 · 자원 단가 줄) + 부름 닫힌 묶음 + SK 문서 · 끊음 · 따로 — `M02_Doc_Copy`(새)
· 부름 문서 = 값 줄이 지금 M01 줄과 같으면 M01 수식 문서 · 다르면 만든 때 줄 변환
- SK 금액 = 문서 셈(`M02_Doc_Sk.on_run`) — 옛 칸(덮개 · 더한줄 …)은 파생(엔진에 넣던 고친 줄 → 문서 · 덮개 없는 바탕 = M01 문서 사본) · 수식 길 고침 = 따로(옛 칸 고침 저장 409 · 재생성 = 파생으로)
- 일위대가 · SC 로직 줄 = 문서 셈(`M02_Labor_Copy.line`) · 줄 옵션 = 문서 규칙(켜쌓기율 변수 r · 고른 자재 = 요소 키 · 별도계상 = 합밖 + 뒤 「재료비의 %」 밑엔 그 줄 금액 더함)
- [단가 갱신] = 자원 단가 줄만(문서 그대로) · [재생성] = 지금 M01 문서로(고친 문서 · 끊음 버림 알림) · 빈 SK = 빈 문서 「—」
- 수식 길 GET/POST …/sangeun/{키}/doc · /preview · /save(층 쓰기 · 서버 정본 셈) — `M02_Doc_Sk_Path` · `M02_Doc_Router`(새 · main 등록)
- B09 구조물 · 서고 사본 = `수식.json` 함께 굳힘(`M02_Doc_B09` · 원본판 안 듦) · B09 셈 그대로(문서 셈 전환 = 802-6)
- 옮기기 `M02_Doc_Migrate`(보기만 · --apply --backup · --restore) · 목록 `docs/raw/요구_1104/_목록_802-4a_M02대조.xlsx` — 층 SK 15 · 일위대가 21 · SC 4 · B09 서고 사본 3 같음 · 빈 SK 2 = 옛 0 → 「—」 · 가져온 식 1 = 802-4b
- 시험 test_802_4a(12) · test_204_2 서고 사본 파일 목록에 수식.json · test_795_1 `_fake` 가 M01 수식 문서도 같이 바꿈(M01 정본 = 수식 문서 — 옛 줄만 바꾼 흉내는 문서 사본에 안 닿음) · 옛 SK 흉내 = 사본 · 수식 둘 다 뗌
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
220 lines
9.3 KiB
Python
220 lines
9.3 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 바꿈 흉내 — 지금 색인 위에 바뀐 줄 · 지운 키(정본 파일 안 고침) · M01 수식 문서도 같이
|
|
(802-4a — 수식 사본 SK 의 M01 정본 = 수식 문서 · 바뀐 줄은 그 줄 변환 문서로)."""
|
|
from M02_MasterTemplete import M02_Doc_Copy as dc
|
|
|
|
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))
|
|
docs = {k: v for k, v in dc.folder().items() if k not in drop}
|
|
docs |= {str(r["키"]): dc.cv.convert(fake, copy.deepcopy(r))[0] for r in rows if "호표" in r}
|
|
monkeypatch.setattr(dc, "folder", lambda: docs)
|
|
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 not in ("사본", "수식")} # 795 앞 SK = 둘 다 없음
|
|
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)
|