Merge branch 'sub_laptop_2' of C:/Aislo_wt/sub_laptop_2 into main_laptop_1
This commit is contained in:
@@ -82,7 +82,8 @@ def test_사전조합_고름_사본_들여옴_새_호표_다시_고르면_재사
|
||||
folder = made["폴더"]
|
||||
assert made == {"폴더": f"precombo@system@-@{world['key']}", "만듦": True}
|
||||
copy = library.library_dir(root) / folder
|
||||
assert {p.name for p in copy.iterdir()} == {"일위대가.json", "단가.json", "manifest.json"}
|
||||
names = {"일위대가.json", "단가.json", "수식.json", "manifest.json"} # 수식 사본(802-4a)
|
||||
assert {p.name for p in copy.iterdir()} == names
|
||||
got, block = _block(root, folder)
|
||||
assert (block["kind"], block["name"], block["unit"]) == ("사전조합", "서고 조합", "㎡")
|
||||
kinds = [(ln["kind"], ln.get("qty")) for ln in block["lines"]]
|
||||
|
||||
@@ -32,12 +32,18 @@ def _live():
|
||||
|
||||
|
||||
def _fake(monkeypatch, rows=(), drop=()):
|
||||
"""M01 바꿈 흉내 — 지금 색인 위에 바뀐 줄 · 지운 키(정본 파일 안 고침)."""
|
||||
"""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
|
||||
|
||||
|
||||
@@ -165,7 +171,7 @@ def test_old_sk_without_copy_old_path(world, monkeypatch):
|
||||
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 != "사본"}
|
||||
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)
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
# ruff: noqa: F811 — 임시 층 fixture(world)를 test_m02_labor_ref 에서 받아 씀
|
||||
"""802-4a M02 단가산출 · 일위대가 · B09 사본 = 수식 문서 — 임시 storage · M01 은 메모리 덮개.
|
||||
|
||||
① 찍기 = M01 문서 사본(금액 = M01 문서 셈) ② 고침 = 사본 문서(M01 안 바뀜 · 옛 칸 고침 409)
|
||||
③ [단가 갱신] = 자원 단가만(문서 그대로) ④ [재생성] = 지금 M01 문서 · 고친 것 버림
|
||||
⑤ 단가산출 연결 = [재생성] 때 따라감 · 끊기 = 사본 안에 굳힘 ⑥ SC · 일위대가 = 문서 셈(옛 금액 같음 ·
|
||||
줄 규칙 켜쌓기율 · 고른 자재 · 별도계상) ⑦ B09 사본 `수식.json` ⑧ 권한 ⑨ 지움 견딤 · 빈 SK 「—」.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
|
||||
import pytest
|
||||
from fastapi import HTTPException
|
||||
|
||||
from M02_MasterTemplete import M02_Doc_B09 as doc_b09
|
||||
from M02_MasterTemplete import M02_Doc_Copy as dc
|
||||
from M02_MasterTemplete import M02_Doc_Router as router
|
||||
from M02_MasterTemplete import M02_Doc_Sk as dsk
|
||||
from M02_MasterTemplete import M02_Doc_Sk_Path as path
|
||||
from M02_MasterTemplete import M02_Item_Snapshot as snap
|
||||
from M02_MasterTemplete import M02_Item_Source as source
|
||||
from M02_MasterTemplete import M02_Labor_Doc as labor
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
||||
from resources.tester.test_795_1_sk_copy import _calc, _changed, _doc, _fake, _live, _save, _sk
|
||||
from resources.tester.test_795_2_sc_copy import LB, PCT, _double, _labor, _sc
|
||||
from resources.tester.test_795_2_sc_copy import _calc as _sc_calc
|
||||
from resources.tester.test_m02_labor_ref import world # noqa: F401 — 같은 임시 층
|
||||
|
||||
FORM = "GC000211" # 합판거푸집(옵션 많음)
|
||||
BASIC = "GC001148" # 굴착기 시간당
|
||||
ADD = {"종류": "로직", "로직": BASIC, "수량": 2, "입력값": {}, "이름": "sub2임시 굴착기"}
|
||||
|
||||
|
||||
def _sum(folder, key):
|
||||
return _calc(folder, _doc(folder, key))["기준"]["계"]
|
||||
|
||||
|
||||
def _at(folder, fn, *args):
|
||||
with store.at(folder):
|
||||
return fn(*args)
|
||||
|
||||
|
||||
def _twice(doc, at=0):
|
||||
"""금액 줄 하나 수량 × 2 — 사용자 고침 흉내."""
|
||||
doc = copy.deepcopy(doc)
|
||||
line = doc["금액"][at]
|
||||
line["수량"] = f"({line.get('수량') or 1}) × 2"
|
||||
return doc
|
||||
|
||||
|
||||
def test_stamp_is_m01_doc_copy(world):
|
||||
"""① 만들 때 M01 문서를 찍음 — 키 = SK · 옵션 값 · 금액 = M01 문서 셈 = 옛 금액."""
|
||||
me = world["personal"]
|
||||
key = _sk(me, 바탕={"로직": FORM}, 입력값={"유형": "복잡", "사용횟수": "3회"})
|
||||
got = _doc(me, key)[dc.KEY]
|
||||
root, m01 = got["문서"], dc.folder()[FORM]
|
||||
assert root["키"] == key and [v["기호"] for v in root["변수"]] == [
|
||||
v["기호"] for v in m01["변수"]
|
||||
]
|
||||
assert next(v for v in root["변수"] if v["열쇠"] == "유형")["연결"]["값"] == "복잡"
|
||||
assert not got.get("따로") and got["로직"] and got["단가"]
|
||||
m01_calc = dc.mc.calc(root, dc.ml.Links(_live(), dc.folder()))
|
||||
assert _sum(me, key) == m01_calc["계"]
|
||||
token = dsk.OLD.set(True)
|
||||
try:
|
||||
assert _sum(me, key) == m01_calc["계"] # 옛 엔진 금액과 같음
|
||||
finally:
|
||||
dsk.OLD.reset(token)
|
||||
read = _at(me, path.read, key)
|
||||
assert read["셈"]["계"] == m01_calc["계"] and read["사본"]["따로"] is False
|
||||
|
||||
|
||||
def test_edit_goes_to_copy_doc(world):
|
||||
"""② 고침 = 그 사본 문서(따로) · M01 문서 그대로 · 옛 칸 고침 저장 409 · 다시 열어 같음."""
|
||||
me = world["personal"]
|
||||
key = _sk(me, 바탕={"로직": PCT})
|
||||
was, m01_before = _sum(me, key), dc.fp(dc.folder()[PCT])
|
||||
read = _at(me, path.read, key)
|
||||
pre = _at(me, path.preview, key, {"문서": _twice(read["문서"])})
|
||||
assert pre["전"]["계"] == was and pre["후"]["계"] > was
|
||||
saved = _at(me, path.save, key, {"문서": _twice(read["문서"]), "판": read["판"]})
|
||||
assert saved["사본"]["따로"] is True and saved["셈"]["계"] == pre["후"]["계"]
|
||||
assert _sum(me, key) == pre["후"]["계"] # SK 금액(일위대가가 쓰는 값) = 고친 문서
|
||||
assert dc.fp(dc.folder()[PCT]) == m01_before
|
||||
assert _at(me, path.read, key)["문서"] == saved["문서"]
|
||||
doc = _doc(me, key)
|
||||
with pytest.raises(store.StoreError) as e:
|
||||
_save(me, key, {**doc, "입력값": {"x": 1}, "덮개": {"labor:LB000036#0": {"qty": 3}}})
|
||||
assert e.value.status == 409
|
||||
_save(me, key, {**doc, "이름": "sub2임시 이름만"}) # 머리 칸 = 그대로 받음
|
||||
assert _sum(me, key) == pre["후"]["계"]
|
||||
with pytest.raises(store.StoreError) as e: # 판 다름
|
||||
_at(me, path.save, key, {"문서": read["문서"], "판": read["판"]})
|
||||
assert e.value.status == 409
|
||||
|
||||
|
||||
def test_price_refresh_values_only(world, monkeypatch):
|
||||
"""③ [단가 갱신] = 자원 단가만 — M01 문서 바뀜은 안 따라감 · 바뀐자원 · 누르기 전 그대로."""
|
||||
me = world["personal"]
|
||||
key = _sk(me, 바탕={"로직": PCT})
|
||||
was = _sum(me, key)
|
||||
root = _at(me, path.read, key)["문서"]
|
||||
_fake(monkeypatch, [_changed(_live().logic(PCT)), _double(LB)])
|
||||
assert _sum(me, key) == was
|
||||
pre = _at(me, path.preview, key, {"할일": "단가갱신"})
|
||||
assert [x["키"] for x in pre["바뀐자원"]] == [LB] and pre["문서"] == root
|
||||
fresh = _sum(me, _sk(me, 바탕={"로직": PCT})) # 새 문서 + 새 단가
|
||||
assert pre["후"]["계"] != was and pre["후"]["계"] != fresh
|
||||
read = _at(me, path.read, key)
|
||||
got = _at(me, path.save, key, {"할일": "단가갱신", "판": read["판"]})
|
||||
assert got["셈"]["계"] == pre["후"]["계"] == _sum(me, key) and got["문서"] == root
|
||||
assert got["사본"]["단가새값"] is False and PCT in got["사본"]["문서새판"]
|
||||
|
||||
|
||||
def test_regen_takes_m01_doc_drops_edits(world, monkeypatch):
|
||||
"""④ [재생성] = 지금 M01 문서로 · 고친 문서 버림(알림) · 파생으로 돌아감."""
|
||||
me = world["personal"]
|
||||
key = _sk(me, 바탕={"로직": PCT})
|
||||
read = _at(me, path.read, key)
|
||||
_at(me, path.save, key, {"문서": _twice(read["문서"], 1), "판": read["판"]})
|
||||
_fake(monkeypatch, [_changed(_live().logic(PCT))])
|
||||
fresh = _sum(me, _sk(me, 바탕={"로직": PCT}))
|
||||
pre = _at(me, path.preview, key, {"할일": "재생성", "문서": {"아무": "버려짐"}})
|
||||
assert pre["버림"]["문서고침"] is True and pre["후"]["계"] == fresh
|
||||
read = _at(me, path.read, key)
|
||||
got = _at(me, path.save, key, {"할일": "재생성", "판": read["판"]})
|
||||
assert got["사본"]["따로"] is False and got["셈"]["계"] == fresh == _sum(me, key)
|
||||
assert got["사본"]["M01새판"] is False and got["사본"]["문서새판"] == []
|
||||
|
||||
|
||||
def test_link_follows_on_regen_unlink_freezes(world, monkeypatch):
|
||||
"""⑤ 단가산출 연결 = 부름 사본(M01 바뀌어도 그대로 · [재생성] 때 따라감) · 끊기 = 사본 안 고침."""
|
||||
me = world["personal"]
|
||||
key = _sk(me, 바탕={"로직": FORM}, 더한줄=[copy.deepcopy(ADD)])
|
||||
was = _sum(me, key)
|
||||
read = _at(me, path.read, key)
|
||||
assert BASIC in read["부름"]
|
||||
cut = {BASIC: _twice(read["부름"][BASIC])}
|
||||
pre = _at(me, path.preview, key, {"끊음": cut})
|
||||
assert pre["후"]["계"] > was
|
||||
saved = _at(me, path.save, key, {"끊음": cut, "판": read["판"]})
|
||||
assert saved["끊음"][BASIC] == {**cut[BASIC], "키": BASIC} and _sum(me, key) == pre["후"]["계"]
|
||||
_fake(monkeypatch, [_changed(_live().logic(BASIC))]) # 원본 바뀜
|
||||
assert _sum(me, key) == pre["후"]["계"] # 끊은 것 · 연결 모두 그대로(누르기 전)
|
||||
with pytest.raises(store.StoreError) as e: # 부르지 않는 키 끊기
|
||||
_at(me, path.preview, key, {"끊음": {"GC999999": read["부름"][BASIC]}})
|
||||
assert e.value.status == 400
|
||||
read = _at(me, path.read, key)
|
||||
pre = _at(me, path.preview, key, {"할일": "재생성"})
|
||||
assert pre["버림"]["끊음"] == [BASIC]
|
||||
got = _at(me, path.save, key, {"할일": "재생성", "판": read["판"]})
|
||||
assert got["끊음"] == {} and got["셈"]["계"] > was # 연결 = 새 원본 따라감
|
||||
|
||||
|
||||
def test_sc_and_labor_use_doc(world, monkeypatch):
|
||||
"""⑥ SC · 일위대가 로직 줄 = 문서 셈(부름 사본) · 옛 엔진 금액과 같음 · M01 바뀌어도 그대로."""
|
||||
me = world["personal"]
|
||||
sc = _sc(me, [{"로직": PCT, "수량": {"손": 1}}])
|
||||
_labor(me, [{"로직": FORM, "수량": {"손": 2}, "입력값": {"유형": "보통"}}])
|
||||
stored = _at(me, store.read, "precombo", sc)["문서"]
|
||||
assert dc.has(stored) and PCT in stored[dc.KEY]["부름"]
|
||||
runs = []
|
||||
real = dc.run
|
||||
monkeypatch.setattr(dc, "run", lambda *a, **k: runs.append(1) or real(*a, **k))
|
||||
new = (_sc_calc(me, sc)["기준합"], _sc_calc(me, "sub2임시")["반영합"])
|
||||
assert runs
|
||||
token = dsk.OLD.set(True)
|
||||
try:
|
||||
assert (_sc_calc(me, sc)["기준합"], _sc_calc(me, "sub2임시")["반영합"]) == new
|
||||
finally:
|
||||
dsk.OLD.reset(token)
|
||||
_fake(monkeypatch, [_changed(_live().logic(PCT))])
|
||||
assert _sc_calc(me, sc)["기준합"] == new[0]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"code, rate, picks, marks",
|
||||
[
|
||||
("GC000261", 90, None, None), # 켜쌓기율 = 변수 r · 수량 × r ÷ 100
|
||||
("GF000156", None, {"3": "MT004176", "4": "MT003834"}, None), # 고른 자재 = 요소 키
|
||||
("GC000665", None, None, None), # 별도계상(기본) = 합밖 · 「재료비의 %」 밑엔 듦
|
||||
],
|
||||
)
|
||||
def test_line_rules_in_doc(code, rate, picks, marks):
|
||||
"""⑥ 일위대가 줄 옵션 = 문서 규칙(브레인 ④) — 옛 `run_line` · `drop_apart` 와 같은 금액."""
|
||||
whole = _live()
|
||||
old = labor.drop_apart(labor.run_line(whole, code, {}, rate, picks), marks)
|
||||
assert old["기준"]
|
||||
apart = dc.apart_of(marks, old.get("재료"))
|
||||
doc = dc.line_doc(dc.folder()[code], rate, picks, apart)
|
||||
if rate:
|
||||
assert dc.RATE_ID in str(doc["금액"][0]["수량"])
|
||||
if apart:
|
||||
assert all(doc["금액"][n].get("합밖") for n in apart)
|
||||
got = dc.mc.calc(doc, dc.ml.Links(whole, dc.folder()), dc.given_of(whole, code, {}))
|
||||
assert {k: got[k] for k in dc.COSTS} == {k: old["기준"][k] for k in dc.COSTS}
|
||||
assert dc.line_doc(dc.folder()[code]) is dc.folder()[code] # 옵션 없음 = 그 문서
|
||||
|
||||
|
||||
def test_b09_copy_has_doc_file(world, monkeypatch):
|
||||
"""⑦ B09 사본 = 단가 묶음 옆 `수식.json`(만든 때 부름) · 원본판엔 안 듦 · M01 바뀌어도 그대로."""
|
||||
me = world["personal"]
|
||||
doc = _labor(me, [{"로직": PCT, "수량": {"손": 2}}])
|
||||
rows = source.labor_doc_rows(doc)
|
||||
files = doc_b09.attach({"일위대가.json": rows, "단가.json": source.price_bundle(rows, me)})
|
||||
made = files[doc_b09.FILE]
|
||||
assert made["그룹"] == "수식문서" and PCT in made["문서"]
|
||||
assert doc_b09.FILE not in snap.source_files(files)
|
||||
assert doc_b09.attach(files) is files # 이미 있음 = 그대로
|
||||
_fake(monkeypatch, [_changed(_live().logic(PCT))])
|
||||
again = doc_b09.build(source.price_bundle(rows, me), rows)
|
||||
assert again["문서"][PCT] == made["문서"][PCT] # 일위대가 수식 사본 = 만든 때
|
||||
|
||||
|
||||
def _session(role="USER"):
|
||||
return {"role": role, "company_id": 7, "user_id": 42}
|
||||
|
||||
|
||||
def test_permission_layer(world):
|
||||
"""⑧ 읽기 = 층 읽기 · 미리 셈 · 저장 = 층 쓰기(마스터 = 시스템 관리자 · 회사 = 회사 관리자)."""
|
||||
me = world["personal"]
|
||||
key = _sk(me, 바탕={"로직": PCT})
|
||||
body = router.DocBody(문서=_at(me, path.read, key)["문서"])
|
||||
for layer in ("system", "company"):
|
||||
for fn in (router.preview_doc, router.save_doc):
|
||||
with pytest.raises(HTTPException) as e:
|
||||
fn(layer, key, body, None, _session())
|
||||
assert e.value.status_code == 403
|
||||
assert router.read_doc("personal", key, None, _session())["셈"]["셈됨"]
|
||||
got = router.preview_doc("personal", key, body, None, _session())
|
||||
assert got["후"] == got["전"]
|
||||
|
||||
|
||||
def test_delete_tolerant_and_blank(world):
|
||||
"""⑨ 무엇을 지워도 오류 없음 — 변수 지움 = 정의 필요 · 금액 「—」 · 저장됨 · 빈 SK = 「—」."""
|
||||
me = world["personal"]
|
||||
key = _sk(me, 바탕={"로직": PCT})
|
||||
read = _at(me, path.read, key)
|
||||
gone = copy.deepcopy(read["문서"])
|
||||
gone["변수"] = [v for v in gone["변수"] if v["열쇠"] != "w_지붕잇기공"]
|
||||
pre = _at(me, path.preview, key, {"문서": gone})
|
||||
assert pre["셈"]["셈됨"] is False and pre["후"] is None
|
||||
assert any(m["갈래"] == "정의 필요" for m in pre["셈"]["표지"])
|
||||
_at(me, path.save, key, {"문서": gone, "판": read["판"]})
|
||||
got = _calc(me, _doc(me, key))
|
||||
assert got["기준"] is None and got["수식셈"]["셈됨"] is False
|
||||
lost = copy.deepcopy(read["문서"])
|
||||
lost["변수"].append({"열쇠": "b", "기호": "b", "연결": {"갈래": "단가산출", "키": "GC999999"}})
|
||||
lost["금액"].append({"이름": "없는 기초", "비목": "묶음", "단가": "b"})
|
||||
pre = _at(me, path.preview, key, {"문서": lost})
|
||||
assert any(m["갈래"] == "연결 끊김" for m in pre["셈"]["표지"])
|
||||
for bad in ({"문서": "글"}, {"문서": {"금액": "x"}}, {"끊음": []}, {"할일": "몰라"}):
|
||||
with pytest.raises(store.StoreError) as e:
|
||||
_at(me, path.preview, key, bad)
|
||||
assert e.value.status == 400
|
||||
empty = _sk(me)
|
||||
got = _calc(me, _doc(me, empty))
|
||||
assert got["기준"] is None and "빈 문서" in got["수식셈"]["까닭"]
|
||||
Reference in New Issue
Block a user