- 셈 구동 떼기: 새 M01_MasterData_UI_Logic_Drive.ts(driveRun · previewText · autoFill · createDrive) — M01 상세는 숨은 시험 계산 칸 단추 대신 구동 함수 · 시험 계산 칸(Logic_Calc)은 로직 복사 화면이 쓰므로 남기고 같은 구동을 부름 · 재료 고름은 rerun 고리 - M02: 새 _Sangeun_Tabs.ts(수식 · 단가산출 상세 보기 고르기) · Detail 의 시험 계산 · 인력/재료/경비 걷음 - 걷은 파일(git rm): M01 LogicLab_Sheet · Logic_Sheet_Model · LogicLab_Formula(.ts .css) · FormulaTokens · LogicLab_Est / M02 Sangeun_Sheet(.ts .css) · SheetEdit · SheetFormula · logic Sheet_Model · Const_Model · Formula_Model · 부품 시트 줄(createCalcSheetRow) · Cover_Model.sheetGroups · Calc 의 M02 고리(answer · shown · held) · 죽은 CSS · 글 - 서버 길 · 문서 칸(변수줄 · 글줄 · 덮개)은 남김 — 금액 그대로 - 낡은 시험: test_790_1(시트 화면 전부) · test_714_3 · test_748_2(+ helper)는 화면이 없어져 지움 · test_794_1 · test_790_2 는 화면 · TS 거울만 덜고 서버 시험 남김 · test_714_2 · 717_2b · 721 · 722(원래 틀린 한 줄 고침) · 734 · 737 · 750_2 · 793_3b · 796_2 · 796_2b 는 보기 둘 · 새 파일에 맞춤 · 새 test_799_2_old_tabs_gone Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
194 lines
8.9 KiB
Python
194 lines
8.9 KiB
Python
# ruff: noqa: F811 — 임시 층 fixture(world)를 test_m02_labor_ref 에서 받아 씀
|
||
"""796-2b M02 단가산출 수식 탭 — 사본 줄 수식 · 고침 = 금액 · 저장 = 사본 위 정본(임시 storage).
|
||
|
||
① 사본 줄 수식 읽기 = SK 금액 ② 고침 미리 셈 = 금액 바뀜(저장 없음) ③ [저장] = 서버가 다시 얹어 셈 ·
|
||
틀린 고침 400 · M01 그대로 ④ 변수 더함 · 지움(쓰는 곳 막음) · 기호 · 첨자 전파 ⑤ 권한 = 층 쓰기
|
||
⑥ 가져온 식 SK 같은 길 ⑦ M01 바뀌어도 그대로 · [재생성] = 고침 다시 얹음 · 못 얹음 알림.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import copy
|
||
|
||
import pytest
|
||
from fastapi import HTTPException
|
||
|
||
from M01_MasterData import M01_MasterData_Store as m01
|
||
from M02_MasterTemplete import M02_Labor_Doc as labor
|
||
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
||
from M02_MasterTemplete import M02_Sangeun_Copy as skc
|
||
from M02_MasterTemplete import M02_Sangeun_Equation as eqm
|
||
from M02_MasterTemplete import M02_Sangeun_Imported as imp
|
||
from M02_MasterTemplete import M02_Sangeun_Router as router
|
||
from resources.tester.test_795_1_sk_copy import (
|
||
_calc,
|
||
_doc,
|
||
_fake,
|
||
_imported_row,
|
||
_live,
|
||
_save,
|
||
_sk,
|
||
_sum,
|
||
)
|
||
from resources.tester.test_m02_labor_ref import world # noqa: F401 — 같은 임시 층
|
||
|
||
KEY = "GC000211" # 합판거푸집 — 중간 시공량 · 형틀목공 · 보통인부 · 호표 0 = 형틀목공
|
||
DOUBLE = [{"할일": "식", "자리": "호표:0", "식": "형틀목공 × 2", "까닭": "sub2임시 시험"}]
|
||
|
||
|
||
def _line(eq, key, kind=None):
|
||
return next(x for x in eq["줄"] if x.get("열쇠") == key and (kind is None or x["갈래"] == kind))
|
||
|
||
|
||
def _read(folder, doc):
|
||
with store.at(folder):
|
||
return eqm.read(doc)
|
||
|
||
|
||
def _preview(folder, doc, edits):
|
||
with store.at(folder):
|
||
return eqm.preview(doc, edits)
|
||
|
||
|
||
def test_read_copy_row_equals_sk_amount(world):
|
||
"""① 사본 줄 수식 — 합계 = SK 금액 = M01 셈 · 키보드 글."""
|
||
me = world["personal"]
|
||
key = _sk(me, 바탕={"로직": KEY})
|
||
got = _read(me, _doc(me, key))
|
||
assert got["ok"], got
|
||
eq = got["수식"]
|
||
assert eq["열쇠"] == KEY and eq["합계"]["계"] == _sum(me, key)
|
||
assert eq["합계"]["계"] == labor.run_line(_live(), KEY, {})["기준"]["계"]
|
||
assert _line(eq, "형틀목공", "호표")["입력글"] == "형틀목공"
|
||
|
||
|
||
def test_edit_changes_amount_save_is_canonical(world):
|
||
"""② · ③ 고침 미리 셈 = 금액 바뀜 · [저장] = 고침 목록 남음 · 서버 셈 = 미리 셈 · M01 그대로."""
|
||
me = world["personal"]
|
||
key = _sk(me, 바탕={"로직": KEY})
|
||
was = _sum(me, key)
|
||
m01_row = copy.deepcopy(_live().logic(KEY))
|
||
got = _preview(me, _doc(me, key), DOUBLE)
|
||
assert got["되나"], got["까닭"]
|
||
assert got["전"]["계"] == was and got["후"]["계"] > was
|
||
assert got["줄"]["호표"][0]["수량"] == "형틀목공 * 2"
|
||
assert _sum(me, key) == was # 미리 셈 = 저장 없음
|
||
doc = {**_doc(me, key), eqm.KEY: DOUBLE}
|
||
_save(me, key, doc)
|
||
saved = _doc(me, key)
|
||
assert saved[eqm.KEY][0]["까닭"] == "sub2임시 시험"
|
||
assert saved["사본"]["로직"][0]["호표"][0]["수량"] == m01_row["호표"][0]["수량"] # 사본 = 원래
|
||
now = _calc(me, saved)
|
||
assert now["기준"]["계"] == got["후"]["계"]
|
||
assert now["원래"]["계"] == was # 원래 대비 = 고침 없는 사본
|
||
assert _read(me, saved)["수식"]["합계"]["계"] == got["후"]["계"]
|
||
assert _live().logic(KEY) == m01_row # M01 정본 안 바뀜
|
||
bad = {**saved, eqm.KEY: [{"할일": "식", "자리": "호표:0", "식": "없는값 × 2"}]}
|
||
with pytest.raises(store.StoreError):
|
||
_save(me, key, bad)
|
||
assert _sum(me, key) == got["후"]["계"]
|
||
|
||
|
||
def test_variables_add_drop_rename(world):
|
||
"""④ 변수 더함 → 식에서 부름 · 쓰는 변수 지움 막음 · 기호 · 첨자 = 쓰는 식 모두."""
|
||
me = world["personal"]
|
||
key = _sk(me, 바탕={"로직": KEY})
|
||
doc = _doc(me, key)
|
||
add = [
|
||
{"할일": "더함", "갈래": "중간", "열쇠": "할증", "식": "1.5", "뒤": "보통인부"},
|
||
{"할일": "식", "자리": "호표:1", "식": "보통인부 × 할증"},
|
||
]
|
||
got = _preview(me, doc, add)
|
||
assert got["되나"], got["까닭"]
|
||
assert got["후"]["노무비"] > got["전"]["노무비"]
|
||
assert "할증" in [v["열쇠"] for v in got["수식"]["변수표"]]
|
||
got = _preview(me, doc, [{"할일": "지움", "열쇠": "시공량"}])
|
||
assert not got["되나"] and "형틀목공" in " ".join(got["까닭"])
|
||
sign = [{"할일": "변수", "열쇠": "시공량", "기호": "Q", "아래": "d", "이름": "하루 시공량"}]
|
||
got = _preview(me, doc, sign)
|
||
assert got["되나"] and got["후"] == got["전"] # 이름만 — 금액 그대로
|
||
assert "Q_d" in _line(got["수식"], "형틀목공", "중간")["입력글"]
|
||
assert "Q_d" in _line(got["수식"], "보통인부", "중간")["입력글"]
|
||
_save(me, key, {**doc, eqm.KEY: sign})
|
||
again = _read(me, _doc(me, key))["수식"]
|
||
assert next(v for v in again["변수표"] if v["열쇠"] == "시공량")["기호"] == "Q"
|
||
|
||
|
||
def _session(role="USER"):
|
||
return {"role": role, "company_id": 7, "user_id": 42}
|
||
|
||
|
||
def test_permission_layer_write(world, monkeypatch):
|
||
"""⑤ 미리 셈 = 그 층 쓰기 — 마스터 = 시스템 관리자 · 회사 = 회사 관리자 · 개인 = 본인."""
|
||
me = world["personal"]
|
||
key = _sk(me, 바탕={"로직": KEY})
|
||
body = router.EquationBody(문서=_doc(me, key), 고침=DOUBLE)
|
||
for layer in ("system", "company"):
|
||
with pytest.raises(HTTPException) as e:
|
||
router.sangeun_equation_preview(layer, body, None, _session())
|
||
assert e.value.status_code == 403
|
||
got = router.sangeun_equation_preview("personal", body, None, _session())
|
||
assert got["되나"] and got["후"]["계"] > got["전"]["계"]
|
||
got = router.sangeun_equation("personal", body, None, _session())
|
||
assert got["ok"] and got["수식"]["합계"]["계"] == got["수식"]["합계"]["계"]
|
||
monkeypatch.setattr(router, "_root", lambda *a, **k: me)
|
||
admin = router.sangeun_equation_preview("system", body, None, _session("SYSTEM_ADMIN"))
|
||
assert admin["되나"]
|
||
|
||
|
||
def test_imported_same_path(world):
|
||
"""⑥ 가져온 식 SK(바탕 null · 가져온.로직) — 같은 읽기 · 고침 · 저장."""
|
||
me = world["personal"]
|
||
with store.at(me):
|
||
key = imp.create(_imported_row(), None, "sub2임시 가져온")["이름"]
|
||
doc = _doc(me, key)
|
||
got = _read(me, doc)
|
||
assert got["ok"], got
|
||
assert got["수식"]["합계"]["계"] == _sum(me, key)
|
||
edit = [{"할일": "식", "자리": "호표:0", "식": "0.5 × 2"}]
|
||
got = _preview(me, doc, edit)
|
||
assert got["되나"], got["까닭"]
|
||
assert got["후"]["노무비"] == pytest.approx(got["전"]["노무비"] * 2, abs=1)
|
||
_save(me, key, {**doc, eqm.KEY: edit})
|
||
now = _calc(me, _doc(me, key))
|
||
assert now["기준"]["노무비"] == pytest.approx(got["후"]["노무비"], abs=1)
|
||
assert _doc(me, key)["가져온"]["로직"]["호표"][0]["수량"] == "0.5" # 엑셀 줄 그대로
|
||
|
||
|
||
def test_m01_change_kept_regen_reapplies(world, monkeypatch):
|
||
"""⑦ M01 바뀌어도 고친 SK 그대로 · [재생성] = 새 사본에 고침 다시 · 자리 없으면 못얹은."""
|
||
me = world["personal"]
|
||
key = _sk(me, 바탕={"로직": KEY})
|
||
_save(me, key, {**_doc(me, key), eqm.KEY: DOUBLE})
|
||
was = _sum(me, key)
|
||
row = copy.deepcopy(_live().logic(KEY))
|
||
row["호표"][1]["수량"] = f"({row['호표'][1]['수량']}) * 3"
|
||
_fake(monkeypatch, [row])
|
||
assert _sum(me, key) == was
|
||
with store.at(me):
|
||
got = skc.preview(_doc(me, key), skc.REGEN)
|
||
assert not got["못얹은"] and got["후"]["계"] > was # 새 판 + 같은 고침
|
||
gone = copy.deepcopy(row)
|
||
gone["호표"] = []
|
||
_fake(monkeypatch, [gone])
|
||
with store.at(me):
|
||
got = skc.preview(_doc(me, key), skc.REGEN)
|
||
assert eqm.LOST in [x["cid"] for x in got["못얹은"]] # 그 밖 = 없어진 줄 M01판(790-2)
|
||
assert m01.loaded()[1].logic(KEY)["호표"] == []
|
||
|
||
|
||
def test_screen_same_part_first_tab():
|
||
"""화면 — M02 수식 = M01 수식 탭 부품 다시 씀(복사 없음) · 첫 탭 · 옛 탭 걷음(799-2) · 저장 = 페이지."""
|
||
import re
|
||
from pathlib import Path
|
||
|
||
root = Path(__file__).resolve().parents[2] / "M02_MasterTemplete"
|
||
mine = (root / "M02_MasterTemplete_Sangeun_Equation.ts").read_text(encoding="utf-8")
|
||
assert "createLabEquation(" in mine and "fetch(" not in mine and "createEqChain" not in mine
|
||
assert "/sangeun/equation${suffix}" in mine and "/sangeun/copy/${how}" in mine
|
||
tabs = (root / "M02_MasterTemplete_Sangeun_Tabs.ts").read_text(encoding="utf-8")
|
||
views = re.findall(r'\{ value: "(\w+)", label: ', tabs)
|
||
# 799-2 — 시트 · 인력·재료·경비 · 시험 계산 · 텍스트 수식 걷음
|
||
assert views == ["equation", "ho"]
|
||
assert 'show("equation");' in tabs
|