221 lines
12 KiB
Python
221 lines
12 KiB
Python
# ruff: noqa: F811 — 임시 층 fixture(world)를 test_m02_labor_ref 에서 받아 씀
|
|
"""811-1 일위대가 단가산출 줄 = SK 참조 한 행(요구 707-1 · 681 · 698) — 임시 storage · M01 읽기만.
|
|
|
|
- 저장 · 읽기 = 안 펼침(SK 참조 그대로) · 셈 = SK 1 단위 × 수량 · 모달 저장 = 그 SK 문서 → 일위대가 금액 따라감
|
|
- [+ 줄] M01 기초 = 그 층 SK 사본(`from-logic` · 같은 내용 = 409 물음 · 씀 · 새로) · 일반 사용자 회사 층 403
|
|
- 다른 층 SK = 이 층 사본 뒤 참조(화면 `Labor_SkPick` · 서버 서고 복사 길)
|
|
- 옮기기 = 로직 줄 → SK + 참조 한 행 · 면적 칸 잇기 옮김 · 전후 금액 같음 · 보기 = 임시 사본 · 실제 저장본 전수
|
|
"""
|
|
|
|
import json
|
|
import re
|
|
from decimal import Decimal
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from fastapi import FastAPI
|
|
from fastapi.testclient import TestClient
|
|
|
|
from common_util.common_util_auth import verify_session
|
|
from M01_MasterData import M01_MasterData_Store_Combo as combo_store
|
|
from M02_MasterTemplete import M02_Labor_Doc as labor
|
|
from M02_MasterTemplete import M02_Labor_Ref as refs
|
|
from M02_MasterTemplete import M02_Labor_SkRow as skrow
|
|
from M02_MasterTemplete import M02_Library_Flat as flat
|
|
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
|
from M02_MasterTemplete import M02_Sangeun_Calc as calc
|
|
from M02_MasterTemplete import M02_Sangeun_Doc as sd
|
|
from M02_MasterTemplete import M02_Sangeun_Router as sk_router
|
|
from M02_MasterTemplete import M02_Template_Layers as layers
|
|
from M02_MasterTemplete.M02_Labor_Expand import Expand
|
|
from resources.tester.test_740_1_library_flat import USER, _make, _read, _whole
|
|
from resources.tester.test_m02_labor_ref import MINE, STONE, TEN, world # noqa: F401
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
M02 = ROOT / "M02_MasterTemplete"
|
|
FORM = "GC000211" # 합판거푸집 — 재료 줄 있음(일위대가 기본 별도계상)
|
|
|
|
|
|
def _src(name: str) -> str:
|
|
return (M02 / name).read_text("utf-8")
|
|
|
|
|
|
def _value(folder, key, given=None):
|
|
with store.at(folder):
|
|
got = Expand(_whole(), refs.read_ref, given or {})({**MINE, "키": key})
|
|
return got["기준"]
|
|
|
|
|
|
def test_참조_한_행_저장_읽기_안_펼침_셈은_SK_곱_수량(world):
|
|
me = world["personal"]
|
|
sk = _make(me, "sangeun", 이름="sub1임시 SK", 바탕={"로직": TEN})
|
|
sc = _make(me, "precombo", 이름="sub1임시 SC", 담은로직=[{"참조": {**MINE, "키": sk}, "수량": {"손": 3}}]) # fmt: skip
|
|
doc = _read(me, "precombo", sc)
|
|
assert doc["담은로직"][0]["참조"] == {**MINE, "키": sk} and "로직" not in doc["담은로직"][0]
|
|
assert flat.flat_doc("precombo", doc)["담은로직"][0]["참조"]["키"] == sk # 읽기 길도 그대로
|
|
one, got = _value(me, sk), _value(me, sc)
|
|
want = labor.mcut.sums([labor.mcut.row({k: v * 3 for k, v in one.items()})])
|
|
assert got["계"] == want["계"] and got["계"] > 0
|
|
|
|
|
|
def test_모달_저장_SK_문서_뒤_일위대가_따라감(world):
|
|
me = world["personal"]
|
|
sk = _make(me, "sangeun", 이름="sub1임시 SK", 바탕={"로직": TEN})
|
|
sc = _make(me, "precombo", 이름="sub1임시 SC", 담은로직=[{"참조": {**MINE, "키": sk}, "수량": {"손": 1}}]) # fmt: skip
|
|
before = _value(me, sc)["계"]
|
|
with store.at(me): # 모달 [저장] = 그 SK 문서 쓰기(`saveTemplate` sangeun)
|
|
now = store.read("sangeun", sk)
|
|
cid = sd.line_ids(_whole().logic(TEN))[0]
|
|
doc = {**now["문서"], "덮개": {cid: {"배율": 2}}}
|
|
store.write("sangeun", sk, now["판"], calc.clean_doc(doc, refs.here()))
|
|
assert _value(me, sc)["계"] != before # 일위대가 금액이 SK 를 따라감
|
|
assert _read(me, "precombo", sc)["담은로직"][0]["참조"]["키"] == sk # 일위대가 문서는 그대로
|
|
|
|
|
|
def test_from_logic_층_SK_사본_같은것_물음_권한(world):
|
|
app = FastAPI()
|
|
app.include_router(sk_router.router)
|
|
app.dependency_overrides[verify_session] = lambda: USER
|
|
client = TestClient(app)
|
|
url = "/api/m02/layers/{}/sangeun/from-logic"
|
|
res = client.post(url.format("personal"), json={"로직": TEN})
|
|
assert res.status_code == 200, res.text
|
|
key, ref = res.json()["이름"], res.json()["참조"]
|
|
assert ref == {**MINE, "키": key}
|
|
doc = _read(world["personal"], "sangeun", key)
|
|
assert doc["바탕"] == {"로직": TEN} and doc["단위"] == "㎡" # 원/10㎡ → ㎡(줄 ÷ 배와 같은 값)
|
|
again = client.post(url.format("personal"), json={"로직": TEN})
|
|
assert again.status_code == 409 # 같은 내용 = 물음(733-2)
|
|
use = client.post(url.format("personal"), json={"로직": TEN, "씀": key})
|
|
assert use.json()["이름"] == key
|
|
new = client.post(url.format("personal"), json={"로직": TEN, "새로": True})
|
|
assert new.json()["이름"] != key
|
|
assert client.post(url.format("personal"), json={"로직": "GF999999"}).status_code == 400
|
|
assert client.post(url.format("company"), json={"로직": TEN}).status_code == 403
|
|
|
|
|
|
def test_M01_기초_SK_값_로직_줄과_같음_재료_별도계상(world):
|
|
"""새 SK 1 단위 = 옛 로직 줄 ÷ 배 · 재료 빈 칸 = 일위대가 기본(별도계상)을 적어 둠."""
|
|
me = world["personal"]
|
|
for code in (TEN, STONE, FORM):
|
|
with store.at(me):
|
|
got = skrow.from_logic(code, new=True)
|
|
line = labor.clean({"담은로직": [{"로직": code, "수량": {"손": 2}}]})
|
|
ref = labor.clean({"담은로직": [{"참조": got["참조"], "수량": {"손": 2}}]})
|
|
a, b = labor.calc(line, {}), labor.calc(ref, {})
|
|
assert a["반영합"] == b["반영합"], code
|
|
doc = _read(me, "sangeun", got["이름"])
|
|
assert doc["자재표시"] and all(v["별도계상"] == "별도계상" for v in doc["자재표시"].values())
|
|
|
|
|
|
def _labor_world(me):
|
|
"""개인 층 조합 일위대가(수량 빈 줄 = 면적 칸) + 면적 문서 + SC(로직 줄 · 손 수량)."""
|
|
lines = [{"로직": TEN, "수량": None}, {"로직": STONE, "수량": {"손": 2}}]
|
|
pill = combo_store.combo_pills({"담은로직": lines})["물량"][0]["키"]
|
|
with store.at(me):
|
|
doc = labor.clean({"담은로직": lines})
|
|
(me / "labor").mkdir(parents=True, exist_ok=True)
|
|
(me / "area").mkdir(parents=True, exist_ok=True)
|
|
(me / "labor" / "c811.json").write_text(json.dumps({**doc, "열": "c811", "단위": "㎡"}, ensure_ascii=False), "utf-8") # fmt: skip
|
|
area = {"시트": [{"id": "s1", "칸": {"A1": {"값": "4"}}, "연결": [{"키": pill, "칸": "A1"}]}]}
|
|
(me / "area" / "c811.json").write_text(json.dumps(area, ensure_ascii=False), "utf-8")
|
|
sc = _make(me, "precombo", 이름="sub1임시 SC", 담은로직=[{"로직": TEN, "수량": {"손": 3}}])
|
|
return pill, sc
|
|
|
|
|
|
def test_옮기기_로직_줄_SK_참조_면적_잇기_전후_같음(world):
|
|
me = world["personal"]
|
|
pill, sc = _labor_world(me)
|
|
with store.at(me):
|
|
before = labor.calc(json.loads((me / "labor" / "c811.json").read_bytes()))["반영합"]
|
|
sc_before = _value(me, sc)
|
|
rows = {r["키"]: r for r in skrow.run_here()}
|
|
assert rows["c811"]["같음"] and rows[sc]["같음"] and rows["c811"]["알약"] == 1
|
|
doc = json.loads((me / "labor" / "c811.json").read_bytes())
|
|
assert all("참조" in x and "로직" not in x for x in doc["담은로직"])
|
|
assert [x["수량"] for x in doc["담은로직"]] == [None, {"손": 2}] # 수량 · 별칭 그대로
|
|
area = json.loads((me / "area" / "c811.json").read_bytes())
|
|
keys = [x["키"] for x in area["시트"][0]["연결"]]
|
|
assert keys == [refs.ref_id(doc["담은로직"][0]["참조"])] and pill not in keys
|
|
with store.at(me):
|
|
assert labor.calc(doc)["반영합"] == before # 원 단위 같음(면적 칸 4)
|
|
assert _value(me, sc) == sc_before
|
|
assert refs.refs_in(_read(me, "precombo", sc))[0]["키"].startswith("SK-")
|
|
made = {k for r in rows.values() for k in r["새SK"]}
|
|
assert len(made) == 3 # SC TEN · 조합 TEN · STONE — 사본(값 묶음)이 달라 공유 안 함
|
|
assert skrow.run_here() == [] # 두 번째 = 옮길 로직 줄 없음
|
|
|
|
|
|
def test_옮기기_다르면_되돌림(world, monkeypatch):
|
|
me = world["personal"]
|
|
_labor_world(me)
|
|
raw = (me / "labor" / "c811.json").read_bytes()
|
|
real = skrow.money
|
|
monkeypatch.setattr(
|
|
skrow, "money", lambda k, key, doc: {"다름": id(doc)} if k == "labor" else real(k, key, doc)
|
|
)
|
|
rows = {r["키"]: r for r in skrow.run_here()}
|
|
assert not rows["c811"]["같음"] and rows["c811"]["공유없음"]
|
|
assert (me / "labor" / "c811.json").read_bytes() == raw # 원래대로
|
|
left = {p.stem for p in (me / "sangeun").glob("SK-*.json")}
|
|
sc_made = set(rows[next(k for k in rows if k != "c811")]["새SK"])
|
|
assert left == sc_made # 되돌린 문서가 만든 SK 는 지움
|
|
|
|
|
|
def test_보기_임시_사본_공유_storage_안_씀(world, tmp_path):
|
|
me = world["personal"]
|
|
_labor_world(me)
|
|
raw = (me / "labor" / "c811.json").read_bytes()
|
|
rows = skrow.migrate(False, work=tmp_path)
|
|
assert rows and all(r["같음"] for r in rows)
|
|
assert (me / "labor" / "c811.json").read_bytes() == raw # 보기 = 진짜 층 안 고침
|
|
assert not (me / "sangeun").exists() or not list((me / "sangeun").glob("SK-*.json"))
|
|
skrow.to_xlsx(rows, tmp_path / "목록.xlsx")
|
|
assert (tmp_path / "목록.xlsx").stat().st_size > 0
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
not any(layers.storage_root().glob("*/*/templates/labor/*.json")), reason="저장본 없음"
|
|
)
|
|
def test_실제_저장본_전수_보기_같음(tmp_path):
|
|
"""공유 storage 층 문서 전부 — 임시 사본에서 옮겨 봄(쓰지 않음) · 줄마다 금액 같음."""
|
|
rows = skrow.migrate(False, work=tmp_path) # 이미 옮겼으면 옮길 줄 0 → 통과
|
|
assert all(r["같음"] for r in rows), [r["키"] for r in rows if not r["같음"]]
|
|
|
|
|
|
def test_마스터_층_단가산출_로직_줄_0():
|
|
"""옮긴 뒤 상태 — 마스터 층 일위대가 · 조합의 담은로직에 로직 줄 0(참조 줄은 SK · SP · SC)."""
|
|
for kind in skrow.KINDS:
|
|
for where, path in layers.layer_files(kind):
|
|
if where != "마스터":
|
|
continue
|
|
lines = [
|
|
x
|
|
for x in json.loads(path.read_bytes()).get("담은로직") or []
|
|
if isinstance(x, dict)
|
|
]
|
|
assert not [x for x in lines if x.get("로직")], (kind, path.stem)
|
|
|
|
|
|
def test_화면_줄_넣기_SK_사본_참조_부품():
|
|
table = _src("M02_MasterTemplete_Labor_Table.ts")
|
|
pick = _src("M02_MasterTemplete_Labor_SkPick.ts")
|
|
assert 'opt.layer !== "project" && !opt.calc ? { layer: opt.layer' in table # 프로젝트 = 옛 길
|
|
assert "return void skFromLogic(own, x.키).then(" in table # M01 기초 = SK 사본
|
|
assert "own && isSkRef(ref)" in table and "void ownSk(own, ref)" in table # 다른 층 = 사본
|
|
assert "if (isSkRef(row.참조) && !opt.calc)" in table # 줄 누름 = SK 수식 모달
|
|
assert "/sangeun/from-logic" in pick and "{ 본뜬 }" in pick and "withSameAsk(" in pick
|
|
assert (
|
|
"fetchSessionUser()" in pick
|
|
) # 주인 null = 자기 층 → 서버 참조 꼴(같은 층 SK 를 사본으로 안 만듦)
|
|
combo = _src("M02_MasterTemplete_Labor_Combo.ts")
|
|
assert "...refCards(doc, calc)" in combo and "key: r.참조.키," in combo # 다이어그램 참조 카드
|
|
for text in (table, pick):
|
|
assert len(text.splitlines()) <= 700
|
|
assert not re.search(r"""\bel\(\s*["'](button|input|select)["']""", text)
|
|
assert len((M02 / "M02_Labor_SkRow.py").read_text("utf-8").splitlines()) <= 700
|
|
|
|
|
|
def test_값_한_벌_소수():
|
|
assert skrow._unit({"기준": {"계": Decimal("1289832")}, "배": 100}) == "12898.32"
|