# ruff: noqa: E501 """흐름 셈 값 찾기 = 옛 셈과 같은 뜻(PLAN 36-2 변환 대조 · sub4) — 마스터 읽기만. 못박는 것 - 표 블록 끝 칸 「·범위규칙」 = 줄마다 경계 말(범위규칙 첫 낱말 · 「이하 포함」 이면 맨 윗줄 이상·이하) - 재료 값이 로직 입력(자재지역 · 계약종별)을 읽음 · 빈 단가 = 알림(blank) — 셈은 계속 - 재료 조건 찾기 field 검색어 = 옛 검색어 찾기(틀의 {자리} 를 채운 글) - 고르기 글 값(「080」)은 글 그대로 — 수로 바꾸면 고르기 밖 """ from __future__ import annotations import json from decimal import Decimal from M01_MasterData import M01_MasterData_Store as store from M01_MasterData import M01_MasterData_Store_Flow as sflow from M02_MasterTemplete.DataFlow import M02_DF_Compute as dc from M02_MasterTemplete.DataFlow import M02_DF_Values as dv def _old(key: str) -> dict: row = store.logic(key)["logic"] given = {k: str(v) for k, v in store.cm.시험입력(row).items()} return {"given": given, "got": store.calc(key, given), "row": row} def test_표_범위규칙_칸() -> None: tables = [ t for d in store.loaded()[0].values() if d.get("그룹") in store.cm.mf.TABLE_GROUPS for t in d.get("표") or [] ] ranged = [t for t in tables if "범위" in (t.get("조건") or {}).values()] for t in ranged[:40]: got = dv._table(str(t["키"])) head, rows = got["rows"][0], got["rows"][1:] assert head[-1] == dv.RANGE_RULE first = str(t.get("범위규칙") or "초과·이하").split(" · ")[0].split(" (")[0] assert {r[-1] for r in rows} <= {first, "이상·이하"}, t["키"] closed = [ t for t in ranged if str(t.get("범위규칙") or "").startswith("이상·미만") and "이하 포함" in str(t.get("범위규칙")) ] for t in closed[:5]: # 맨 윗줄(위 끝이 가장 큰 줄)만 이상·이하 words = [r[-1] for r in dv._table(str(t["키"]))["rows"][1:]] assert "이상·이하" in words and "이상·미만" in words, t["키"] plain = next(t for t in tables if "범위" not in (t.get("조건") or {}).values()) assert dv.RANGE_RULE not in dv._table(str(plain["키"]))["rows"][0] def test_재료는_로직_입력을_읽고_빈_단가는_알림() -> None: old = _old("GC000685") # 모래 — 「지역: 입력」 · 입력 자재지역 line = next(x for x in old["got"]["lines"] if x["이름"] == "모래") cond = next(x["요소"] for x in old["row"]["호표"] if x["이름"] == "모래") assert dv._material(cond, "㎥", old["given"])["value"] == line["단가"] alone = dv._material(cond, "㎥") assert alone["ok"] is False and alone["blank"] is True # 입력이 없으면 그 값만 비움 el = { "ref": "MT030715", "name": "바퀴숫돌", "spec": "", "unit": "개", "mode": "fixed", } # 값 빈 품목 flow = { "boxes": [ { "id": "m", "kind": "input", "name": "재료", "input": {"mode": "group", "group": "재료", "elements": [el], "variables": []}, } ], "links": [], } if dv._price("재료", "MT030715").get("blank"): got = dc.run(flow, {}, lambda k: {}, [], ["t"]) assert got["errors"] == [] and any("바퀴숫돌" in n for n in got["notes"]) def test_검색어_조건_찾기는_옛_검색어_찾기() -> None: old = _old("GF000185") # 레미콘 {굵은골재최대치수}-{강도}-{슬럼프} item = next( x for x in old["row"]["호표"] if isinstance(x["요소"], dict) and "{" in str(x["요소"].get("검색어")) ) want = next(x for x in old["got"]["lines"] if x["이름"] == item["이름"])["단가"] text = store.cm.mf.fill(item["요소"]["검색어"], old["given"]) slot = {"mode": "rule", "field": "검색어", "op": "같음", "unit": item["단위"], "scope": {k: item["요소"][k] for k in ("구분", "상세구분") if item["요소"].get(k)}, "ref": json.dumps(store._dec(item["요소"]), ensure_ascii=False, default=str), "inputs": old["given"]} # fmt: skip assert dv.slot_price(slot, text)["value"] == want assert dv.slot_price({**slot, "ref": None}, text)["value"] == want # 묶음 없이 범위 + 검색어 assert dv._filled("레미콘 {a}-{b}-{c}", "레미콘 25-18-080") == "레미콘 25-18-080" assert dv._filled("싱글코아바렐 {규격}", "BX") == "싱글코아바렐 BX" def test_고르기_글_값은_글_그대로() -> None: assert sflow._value("080", {"080"}) == "080" and sflow._value("080") == Decimal(80) assert sflow._value("3") == Decimal(3) and sflow._value("토사") == "토사"