Files
Aislo/resources/tester/test_796_2b_m02_equation.py
T
eomsangdonandClaude Opus 5.5 c80b5ae9d0 feat(802-4b-2): M02 단가산출 수식 탭 = 공용 수식 문서 화면 · 꺼진 줄 = 계산 변수 거침
- M02 SK 「수식」 탭 = M01 과 같은 공용 화면(`createDocScreen` · 길 앞머리만 `/m02/layers/{층}/sangeun/{키}/doc`) — 옛 M02 수식 나무 화면(`_Sangeun_Equation.ts` · 796-2b · 2c · 2e · 800 · 801 꼴) 걷음
  · 옵션 = SK 문서 값(저장) · [단가 갱신] · [재생성](미리 셈 → 알림 계 전 → 후 · 버리는 것 → 저장) · 「M01 새 판 있음」 배지(`formula_doc_copy.ts`)
  · 단가산출 연결 펼침 머리 「연결 / 끊음」 · [연결 끊기] · [다시 잇기] · 끊은 문서 안 줄 그 자리 고침(`formula_doc_sub.ts`) · 저장 몸 {문서, 끊음, 판}
  · 층 쓰기 권한 없음 = 읽기만(옵션 잠금 · 단추 없음 · 「읽기만」) · 판 바뀜 409 = 사본 같으면 새 판으로 한 번 더 · 저장 뒤 페이지 판 · 상세 탭 `수식` 칸 새로
  · 저장 안 한 SK(일위대가 줄 모달) = 「저장한 단가산출만」 글
- 서버 — 수식 길 답에 `연결`(화면 캐시 기록 · `M02_Doc_Screen.record`) · 연결 후보 · 표 길(층 읽기 · M01 후보 · 표 그대로 · main 등록)
- 꺼진 줄(요구 663-1 마저) — 곱 인수 하나가 선택으로 꺼진 0(만약 0 갈래 · 식 줄 기호면 그 식으로 거침)이면 숨김 · 입력 · 글 0 은 그대로
  · 판정을 순수 파일로(`common_util_formula_doc_view.ts` · Node 로 시험) · 1,518 문서 기본 옵션 꺼짐 158 → 203 줄(꺼진 줄 금액 모두 0) · GC000211 폼타이 아님 = 세 규격 줄 모두
- 화면 파일 700 줄 — 고침 함수 · 옵션 가로 · 연결 펼침을 새 파일로(`formula_doc_edit` · `_options` · `_sub`)
- 시험 test_802_4b2(5 · 헬퍼 helper_802_4b2_off.cjs) · 옛 시험 둘 고침 — test_793_3b · test_796_2b 가 걷은 옛 M02 수식 화면 글을 읽음 → 없음 확인으로

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 20:20:52 +09:00

192 lines
8.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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, "형틀목공", "호표")["입력글"] == "Q_형틀목공"
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 수식 = 공용 수식 문서 화면(802-4b-2 · 옛 수식 나무 화면 걷음) · 첫 탭 · 옛 탭 걷음(799-2)."""
import re
from pathlib import Path
root = Path(__file__).resolve().parents[2] / "M02_MasterTemplete"
assert not (root / "M02_MasterTemplete_Sangeun_Equation.ts").exists()
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