218 lines
11 KiB
Python
218 lines
11 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""793-3b 지킴 — 불러오기 단산 줄 = 가져온 식 단가산출 모달(요구 646 · 647 · 652 갈래 C).
|
|
|
|
- 서버 길 `…/sangeun/from-hopyo` — 올림 때 산근 시트 둠(`실무자료/산근/<HP>.json`) →
|
|
블록 → 793-2a 내보내기 → 793-2 가져온 식 SK(만들 층 · 엑셀 단가 그대로 · 검산 = 엑셀) ·
|
|
같은 내용 = 그 SK 다시 씀 · 산근 없는 옛 HP = 409 · HP 지우면 산근도 지움 ·
|
|
STmate 블록 끝전 = 엑셀 계와 맞는 쪽
|
|
- 화면 소스 — [수정] 모달 단산 줄 · 오른쪽 블록 판 [가져온 식으로 열기] · 시트 모달
|
|
(`Sangeun_Detail`) · 머리 판(검산 · [우리 단가로] / [엑셀 단가로] · [원본 글 보기] ·
|
|
[이음 고르기] · 근거 세 단 · [우리 기초로 바꾸기] · 입력 필요 붉은 칸) ·
|
|
[이 줄에 잇기] = 줄 고른 것 SK · 결과 칩 · 「새」 배지 · 부품 상자만 · 700 줄
|
|
임시 storage(`client` 마당) · M01 원본 그대로.
|
|
"""
|
|
|
|
import re
|
|
from pathlib import Path
|
|
|
|
import helper_m02_hopyo_golden as golden
|
|
import pytest
|
|
|
|
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
|
from M02_MasterTemplete import M02_Sangeun_FromHopyo as fh
|
|
from M02_MasterTemplete import M02_Sangeun_Router as sangeun_router
|
|
from M02_MasterTemplete import M02_Template_Layers as layers
|
|
|
|
# ruff: noqa: F811
|
|
from resources.tester.test_m02_hopyo_excel import _post, client, master_hash # noqa: F401
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
M02 = ROOT / "M02_MasterTemplete"
|
|
API = "/api/m02/layers/personal"
|
|
KEY = "HP-000001"
|
|
MINE = ("Hopyo_Imported", "Hopyo_ImportedPanel", "Hopyo_Check", "Hopyo_CheckRows", "Hopyo_Block")
|
|
|
|
|
|
def _src(name: str) -> str:
|
|
return (M02 / f"M02_MasterTemplete_{name}.ts").read_text(encoding="utf-8")
|
|
|
|
|
|
def _skcount() -> int:
|
|
folder = layers.personal_dir(7, 42) / "sangeun"
|
|
return len(list(folder.glob("*.json"))) if folder.exists() else 0
|
|
|
|
|
|
def _open(client, no=18):
|
|
body = {"층": "personal", "키": KEY, "번": no}
|
|
return client.post(f"{API}/sangeun/from-hopyo", json=body)
|
|
|
|
|
|
# ── 서버 길 ───────────────────────────────────────────────────────────
|
|
def test_단산_블록_가져온_식_만들기_다시씀_지움(client):
|
|
client.app.include_router(sangeun_router.router)
|
|
before = master_hash()
|
|
assert _post(client).status_code == 200
|
|
folder = layers.personal_dir(7, 42)
|
|
assert fh.has(folder, KEY)
|
|
made = _open(client)
|
|
assert made.status_code == 200, made.text
|
|
got = made.json()
|
|
assert got["이름"].startswith("SK-") and got["다시씀"] is False
|
|
assert got["단가기준"] == "엑셀" and got["검산"]["같음"] is True
|
|
assert round(got["검산"]["셈"]["계"]) == 30369 # 북부청임도 단산18 문양거푸집 = 엑셀
|
|
n = _skcount()
|
|
again = _open(client).json()
|
|
assert again["이름"] == got["이름"] and again["다시씀"] is True and _skcount() == n
|
|
with store.at(folder):
|
|
doc = store.read("sangeun", got["이름"])["문서"]
|
|
assert doc["바탕"] is None and doc["가져온"]["원본"]["엑셀금액"]["계"] == 30369
|
|
ten = _open(client, 10).json() # 야면석소운반 — 793-3a 근거 제안 = SK 가져온.근거후보
|
|
with store.at(folder):
|
|
cand = store.read("sangeun", ten["이름"])["문서"]["가져온"]["근거후보"]
|
|
assert cand and cand[0]["기초"] == "GC001140" and cand[0]["단"] in ("확정", "근접", "사람")
|
|
assert _open(client, 99999).status_code == 404
|
|
fh._path(folder, KEY).unlink() # 산근 없는 옛 HP = 다시 올림
|
|
old = _open(client)
|
|
assert old.status_code == 409 and old.json()["detail"] == fh.RELOAD
|
|
assert _post(client).status_code == 200 and fh.has(
|
|
folder, KEY
|
|
) # 같은 엑셀 다시 올림 = 산근 채움
|
|
assert client.delete(f"{API}/hopyo/{KEY}").status_code == 200
|
|
assert not fh.has(folder, KEY) # HP 지우면 산근도
|
|
assert master_hash() == before
|
|
|
|
|
|
RULES = [("봉화", "D00102", "비목버림"), ("동부청영월", "D00003", "합버림_큰나머지")]
|
|
|
|
|
|
@pytest.mark.parametrize(("tag", "code", "rule"), RULES)
|
|
def test_STmate_끝전_엑셀_계와_맞는_쪽(tag, code, rule):
|
|
from M02_MasterTemplete import M02_San_Book as book
|
|
from M02_MasterTemplete import M02_San_Stmate as stm
|
|
|
|
if not golden.FILES[tag].exists():
|
|
pytest.skip(f"실무 엑셀 없음 {tag}")
|
|
data = book.load(golden.FILES[tag])
|
|
blk = next(b for b in data["블록"] if b["코드"] == code)
|
|
assert fh._rule(blk, stm.parse_block(blk["몸"]), data["단가"]) == rule
|
|
|
|
|
|
# ── 화면 소스 ─────────────────────────────────────────────────────────
|
|
def test_단산_줄_길_잇기():
|
|
check, rows, block = _src("Hopyo_Check"), _src("Hopyo_CheckRows"), _src("Hopyo_Block")
|
|
assert "openImported({" in check and "onLink: (p) => kit.edit(row, [p])" in check
|
|
assert "imported }" in check.split("const ctx = ")[1].split(";")[0]
|
|
assert 'L("M02_CheckImported")' in rows and "row.번 !== undefined && ctx.imported" in rows
|
|
assert 'import("./M02_MasterTemplete_Hopyo_Imported")' in block # 돌림 import 피함
|
|
assert 'L("M02_CheckImported")' in block and "dataset.checkNew" in block
|
|
imp = _src("Hopyo_Imported")
|
|
assert '"/sangeun/from-hopyo"' in imp and "/sangeun/imported/${how}" in imp
|
|
assert "mountSangeunDetail(" in imp and "saveTemplate(" in imp and "openModal({" in imp
|
|
assert "skPick(sk" in imp and "o.onLink" in imp # [이 줄에 잇기] = 줄 고른 것 SK
|
|
for how in ('run("price"', 'run("link"', 'run("to-base"'):
|
|
assert how in imp, how
|
|
sheet = _src("Sangeun_Sheet") # 상태줄 원래 = 가져온 식이면 「엑셀 원래」
|
|
assert 'Excel: ["엑셀 원래"' in sheet and '"가져온" in d.ans ? "Excel" : "Origin"' in sheet
|
|
locale = (ROOT / "ui_template/ui_template_locale_m2_judge.ts").read_text(encoding="utf-8")
|
|
assert 'M02_CheckImported: ["가져온 식으로 열기"' in locale
|
|
|
|
|
|
def test_머리_판_결과_칩():
|
|
panel = _src("Hopyo_ImportedPanel")
|
|
for word in (
|
|
"ToOurs",
|
|
"ToExcel",
|
|
"Source",
|
|
"Link",
|
|
"ToBase",
|
|
"PickBase",
|
|
"Need",
|
|
"Same",
|
|
"Diff",
|
|
):
|
|
assert f'tx("{word}")' in panel, word
|
|
assert "newBadge()" in panel and "data-basis-tiers" in panel and "TIERS.map(" in panel
|
|
assert 'f.setError(tx("Need"))' in panel # 입력 필요 = 붉은 칸
|
|
model = (M02 / "logic/M02_Hopyo_Imported_Model.ts").read_text(encoding="utf-8")
|
|
assert '["확정", "근접", "사람"]' in model and "필요: need" in model
|
|
assert "입력 필요 ${need.length}" in model and '"가져온 식 있음"' in model
|
|
rows = _src("Hopyo_CheckRows")
|
|
assert "rowChips(ctx.kit.picksOf(row))" in rows and "dataset.judgeResult" in rows
|
|
assert not re.search(r"from \"@ui|document\.|fetch\(|sessionStorage\.", model) # 순수
|
|
|
|
|
|
def test_부품상자만_700줄_공용_안_고침():
|
|
for name in MINE:
|
|
text = _src(name)
|
|
assert len(text.splitlines()) <= 700, name
|
|
assert not re.search(r"""\bel\(\s*["'](button|input|select)["']""", text), name
|
|
assert not re.search(r"""createElement\(\s*["'](button|input|select)["']""", text), name
|
|
assert "#" not in "".join(re.findall(r"color:\s*[^;]+", text)), name
|
|
for name in ("Labor_Rows", "Labor_SangeunModal", "Sangeun_Detail", "Sangeun_Sheet"):
|
|
assert "Hopyo_Imported" not in _src(name), name # 공용 쪽은 새 모달을 모름
|
|
|
|
|
|
def test_가져온_식_시트_M01바뀜_없음(client):
|
|
"""가져온 식 SK 는 M01 짝이 없음 — 저장 M01판이 있어도 시트 「M01 바뀜」 · 짝잃음 안 띄움."""
|
|
from M02_MasterTemplete import M02_Sangeun_Calc as calc
|
|
|
|
client.app.include_router(sangeun_router.router)
|
|
assert _post(client).status_code == 200
|
|
name = _open(client).json()["이름"]
|
|
folder = layers.personal_dir(7, 42)
|
|
with store.at(folder):
|
|
doc = store.read("sangeun", name)["문서"]
|
|
spots = list(calc.calc_doc(doc)["시트"]["자리"])
|
|
ans = calc.calc_doc({**doc, "M01판": {k: "다름" for k in [*spots, "x"]}})
|
|
sheet = ans["시트"]
|
|
assert sheet["자리"] and not any(x["M01바뀜"] for x in sheet["자리"].values())
|
|
assert sheet["짝잃음"] == []
|
|
|
|
|
|
def test_우리_기초로_바꾸기_환산_입력_필요(tmp_path):
|
|
"""북부청 단산10 야면석소운반(㎡) → GC001140(원/자재 단위) — 환산 없으면 후 None ·
|
|
필요 「환산」 · 블록 풀이 바인딩 옵션 + 계수 1 = 엑셀 94,227(품 같음)."""
|
|
from M02_MasterTemplete import M02_Hopyo_Store as hs
|
|
from M02_MasterTemplete import M02_Sangeun_Imported as imp
|
|
|
|
path = golden.FILES["북부청임도"]
|
|
if not path.exists():
|
|
pytest.skip("실무 엑셀 없음")
|
|
hs._dir(tmp_path).mkdir(parents=True)
|
|
(hs._dir(tmp_path) / f"{KEY}.json").write_text(f'{{"키": "{KEY}", "판": {hs.BOOK}}}', "utf-8")
|
|
fh.keep(tmp_path, KEY, path.name, path.read_bytes())
|
|
out = fh.emit(tmp_path, KEY, 10)
|
|
doc = {"키": "SK-000000", "이름": "야면석소운반", "단위": "㎡", "바탕": None, "입력값": {}}
|
|
doc[imp.KEY] = {"로직": out["로직"], "단가기준": "엑셀", "원본": out["원본"]}
|
|
given = {"운반거리": 6, "경사각도": 35, "운반회수": 42, "운반무게": 575, "회당운반량": 25}
|
|
given |= {"재료종별": "암석", "재료형상": "해당 없음", "재료갈래": "토사류", "운반로": "양호"}
|
|
given |= {"실작업시간": 0}
|
|
bare = imp.to_base(doc, "GC001140", given)
|
|
assert bare["후"] is None and bare["필요"] == ["환산"] and round(bare["전"]["계"]) == 94227
|
|
got = imp.to_base(doc, "GC001140", given, {"계수": 1})
|
|
assert (
|
|
"필요" not in got and round(got["후"]["계"]) == 94227 and got["문서"]["환산"] == {"계수": 1}
|
|
)
|
|
assert got["문서"]["바탕"] == {"로직": "GC001140"} and got["문서"][imp.KEY]["바꿈"]["전"]
|
|
|
|
|
|
def test_이_줄에_잇기_옮기기_SK_참조(client):
|
|
"""[이 줄에 잇기] 고른 것 {갈래 단가산출, 키 SK}(층 · 주인 없음) → 옮기기 = 그 SK 참조 줄."""
|
|
from M02_MasterTemplete import M02_Hopyo_Judge as judge
|
|
|
|
client.app.include_router(sangeun_router.router)
|
|
assert _post(client).status_code == 200
|
|
folder = layers.personal_dir(7, 42)
|
|
judge.solve_all(folder, KEY)
|
|
sk = _open(client, 10).json()["이름"]
|
|
pick = {"갈래": "단가산출", "키": sk, "이름": "야면석소운반", "단위": "㎡"} # 화면 skPick 꼴
|
|
pick |= {"입력값": {}, "출처": {}, "몫": None, "필요": [], "표지": ["가져온 식 있음"]}
|
|
body = {"층": "personal", "호표": {"2": {"3": [pick]}}, "블록": {}}
|
|
got = client.post(f"{API}/hopyo/{KEY}/move", json=body)
|
|
assert got.status_code == 200, got.text
|
|
sc = got.json()["만듦"][0]["키"]
|
|
with store.at(folder):
|
|
lines = store.read("precombo", sc)["문서"]["담은로직"]
|
|
refs = [x["참조"]["키"] for x in lines if x.get("참조")]
|
|
assert got.json()["막힘"] == [] and refs == [sk], got.json()["막힘"]
|