- 기계 보기 부품(A00_Common/line_stack_machine_sheet) — 836-1 제어 구역 그대로(기계 표 꼴 훅 sheet · 잠금 lock) + 기본 정보 · 손료 근거 접이 카드 + 비슷한 기계에서 시작 · 값 바꾸면 브라우저 계산기로 즉시 - 서버 POST /api/m02/machine-sheet(M02_MyMachine_Sheet) — 식 고름 + 식 문서 + 처음 줄 값 + 연결 기록 · 쓰기 없음 · 파이썬 bind ↔ TS bindDoc 짝 - 덮음 ↔ 기계 줄(common_util_machine_sheet) — 고침 없음 = 줄 바이트 같음 · 내 기계 바로 · 직접 자리 맞춤 - 내 기계 = M01 에 없는 기계만 — 화면 만들기 · 고치기에서 M01 과 같은 이름 · 규격 409(불러오기 자동 생성은 그대로) · [새 기계] · 고침 열림 - 옛 기계 양식 부품(ui_template_machine_form) 지움 — 쓰는 곳 0(부품 모음 견본 줄도 걷음) - 낡은 시험 test_821_3 test_stack_line_copy — 836-2 자동 따라옴(내 기계 고치면 SK 금액 따라감 · 갱신 할 것 없음)에 맞춤 - 지킴 시험 test_840_2 — M01 기계 655 대 박은 식 셈 = 요소 셈 · 줄 그대로 · 굴착기 덮음 · 내 기계 꼴 · M01 같은 이름 막음 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
201 lines
9.4 KiB
Python
201 lines
9.4 KiB
Python
"""840-2 기계 보기 지킴 시험 — 값 박음 · 덮음 ↔ 기계 줄 · M01 같은 이름 막음 · 쓰기 없음.
|
|
|
|
짝 = `M02_MasterTemplete/M02_MyMachine_Sheet.py` `bind` ↔ `common_util/common_util_machine_sheet.ts` `bindDoc`
|
|
(+ 덮음 ↔ 줄 `coverFrom` · `applyCover` — 화면 몫 · Node 로 돌림).
|
|
① M01 기계 655 대 전수 — 식 고름 → 값 박은 식 셈 = 기계 표 요소로 읽는 셈(노 · 재 · 경 · 계 같음) · 식 없음 = 까닭.
|
|
② 655 대 전수(TS) — 고침 없음 = 덮음 없음 · 줄 JSON 같음(열고 닫아도 M01 기계 파일 그대로) · 박음 거울.
|
|
③ 굴착기 1.0㎥ — 가격 · 조종원(2 인 · 직종 · 뺌) · 잡품 율 덮음 → 줄 칸 · 덮음 얹고 박은 셈 = 줄 고친 셈.
|
|
④ 내 기계 꼴 — 바로(상각비계수 → 손료계수) · 직접(시간당) · 처음 값으로 되돌리면 줄 같음.
|
|
⑤ M01 과 같은 이름 · 규격 = 409 · 다른 이름 = 통과(검사만 · 쓰기 없음).
|
|
"""
|
|
|
|
import copy
|
|
import importlib
|
|
import json
|
|
import os
|
|
import subprocess
|
|
from decimal import Decimal as D
|
|
|
|
import pytest
|
|
|
|
from M01_MasterData import M01_MasterData_Store as m01 # scripts 길을 둠
|
|
from M01_MasterData import M01_MasterData_Doc as m01doc
|
|
from M02_MasterTemplete import M02_Machine_Hourly as mh
|
|
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
|
from M02_MasterTemplete import M02_MyMachine as mym
|
|
from M02_MasterTemplete import M02_MyMachine_Sheet as ms
|
|
|
|
mc = importlib.import_module("master_doc_calc")
|
|
ml = importlib.import_module("master_doc_link")
|
|
mcv = importlib.import_module("master_doc_cover")
|
|
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
COSTS = ("노무비", "재료비", "경비", "계")
|
|
DIG = "EQ000022" # 굴착기(무한궤도) 1.0㎥ 0201-0100 · GC000267
|
|
|
|
|
|
def _plain(v):
|
|
return json.loads(json.dumps(v, ensure_ascii=False, default=ms._plain))
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def world():
|
|
"""M01 기계 줄 · 식 고름 · 식 문서 — 한 번만(마스터 층 · 쓰기 없음)."""
|
|
with store.at(store.FOLDER):
|
|
rows = m01.loaded()[0]["기계.json"]["줄"]
|
|
docs = m01doc.docs()
|
|
links = ml.Links(m01.loaded()[1], docs)
|
|
picks = {}
|
|
for r in rows:
|
|
got = mh.pick({**r, "값": m01._dec(r.get("값") or {})})
|
|
picks[r["키"]] = str(got["키"]) if got and str(got["키"]) in docs else None
|
|
yield {"rows": rows, "docs": docs, "links": links, "picks": picks}
|
|
|
|
|
|
def _node(payload):
|
|
proc = subprocess.run(
|
|
["node", os.path.join(HERE, "helper_840_2_machine_sheet.cjs")],
|
|
input=json.dumps(_plain(payload), ensure_ascii=False),
|
|
capture_output=True,
|
|
text=True,
|
|
encoding="utf-8",
|
|
check=True,
|
|
)
|
|
return json.loads(proc.stdout)
|
|
|
|
|
|
def _money(a):
|
|
return {k: a.get(k) for k in COSTS}
|
|
|
|
|
|
# ── ① 655 대 금액 ──────────────────────────────────────────────────────
|
|
def test_655대_박은_식_셈_같음(world):
|
|
links, docs = world["links"], world["docs"]
|
|
same = none = 0
|
|
for r in world["rows"]:
|
|
key = world["picks"][r["키"]]
|
|
if key is None:
|
|
none += 1
|
|
continue
|
|
given = {"기계": str(r.get("원문번호"))}
|
|
before = mc.calc(docs[key], links, given)
|
|
after = mc.calc(ms.bind(docs[key], m01._dec(r.get("값") or {})), links, given)
|
|
assert _money(after) == _money(before), (r["키"], key)
|
|
same += before["셈됨"]
|
|
assert len(world["rows"]) == 655 and same >= 600 and none <= 44
|
|
|
|
|
|
def test_식_없는_기계_까닭(world):
|
|
with store.at(store.FOLDER):
|
|
for r in world["rows"]:
|
|
if world["picks"][r["키"]] is None:
|
|
got = ms.sheet(_plain(r))
|
|
assert got["키"] is None and got["문서"] is None and got["까닭"]
|
|
return
|
|
pytest.skip("식 없는 기계 없음")
|
|
|
|
|
|
# ── ② 655 대 덮음 ↔ 줄(TS) ──────────────────────────────────────────────
|
|
def test_655대_고침_없음_줄_같음_박음_거울(world):
|
|
docs, targets, want = {}, [], []
|
|
for r in world["rows"]:
|
|
key = world["picks"][r["키"]]
|
|
if key is None:
|
|
continue
|
|
docs[key] = docs.get(key) or _plain(world["docs"][key])
|
|
bind = r["키"] in (DIG, "EQ000001", world["rows"][-1]["키"])
|
|
targets.append({"식": key, "처음": _plain(r), "개인": False,
|
|
**({"박음": _plain(r["값"])} if bind else {})}) # fmt: skip
|
|
want.append(_plain(ms.bind(world["docs"][key], r["값"])) if bind else None)
|
|
got = _node({"문서들": docs, "대상": targets})
|
|
for t, g, w in zip(targets, got, want):
|
|
assert g["덮음"] == {}, t["처음"]["키"]
|
|
assert json.dumps(g["줄"], ensure_ascii=False) == json.dumps(t["처음"], ensure_ascii=False)
|
|
if w is not None:
|
|
assert g["박음"] == w
|
|
|
|
|
|
# ── ③ 굴착기 덮음 → 줄 · 셈 ────────────────────────────────────────────
|
|
def test_굴착기_덮음_줄_칸_셈(world):
|
|
row = _plain(next(r for r in world["rows"] if r["키"] == DIG))
|
|
doc = world["docs"]["GC000267"]
|
|
given = {"기계": row["원문번호"]}
|
|
jobs = ms._jobs()
|
|
lb = next(k for k, v in jobs.items() if v == "1002")
|
|
cases = [
|
|
({"값": {"가격": 150000}}, {"가격": 150000}),
|
|
({"조종원": [{"키": "LB:1048", "인": 2}]}, {"조종원": 2}),
|
|
({"조종원": [{"키": lb, "인": 1}]}, {"조종원직종": "1002"}),
|
|
({"조종원": []}, {"조종원": 0}),
|
|
({"잡품": [{"줄": "주연료", "율": 30}]}, {"잡재료율": 30}),
|
|
]
|
|
targets = [
|
|
{"식": "GC000267", "처음": row, "덮음": c, "개인": False, "직종": jobs} for c, _ in cases
|
|
]
|
|
got = _node({"문서들": {"GC000267": _plain(doc)}, "대상": targets})
|
|
for (cover, change), g in zip(cases, got):
|
|
want = copy.deepcopy(row)
|
|
want["값"].update(change)
|
|
assert g["줄"] == want, cover
|
|
shaped = ms.bind(
|
|
mcv.cover_doc(doc, given, {row["원문번호"]: m01._dec(cover)}), m01._dec(row["값"])
|
|
)
|
|
edited = ms.bind(doc, m01._dec(g["줄"]["값"]))
|
|
a, b = mc.calc(shaped, world["links"], given), mc.calc(edited, world["links"], given)
|
|
assert a["셈됨"] and _money(a) == _money(b), cover
|
|
# 지금 줄(초안) → 덮음 — 처음 줄 대비 고친 칸만
|
|
cur = copy.deepcopy(row)
|
|
cur["값"].update({"가격": 150000, "조종원": 2, "잡재료율": 30})
|
|
made = _node({"문서들": {"GC000267": _plain(doc)},
|
|
"대상": [{"식": "GC000267", "처음": row, "지금": cur, "개인": False}]})[0] # fmt: skip
|
|
assert made["덮음"] == {
|
|
"값": {"가격": 150000},
|
|
"조종원": [{"키": "LB:1048", "인": 2}],
|
|
"잡품": [{"줄": "주연료", "율": 30}],
|
|
}
|
|
assert made["줄"] == cur
|
|
|
|
|
|
# ── ④ 내 기계 꼴 ───────────────────────────────────────────────────────
|
|
def test_내기계_바로_직접(world):
|
|
easy = {"이름": "t", "규격": "", "방식": "바로",
|
|
"값": {"가격": 1000, "손료계수": 2000, "주연료": 5, "주연료종류": "경유"}} # fmt: skip
|
|
hour = {"이름": "t", "규격": "", "방식": "직접", "값": {},
|
|
"시간당": {"노무비": 1, "재료비": 2, "경비": 3}} # fmt: skip
|
|
with store.at(store.FOLDER):
|
|
a, b = ms.sheet(easy, mine=True), ms.sheet(hour, mine=True)
|
|
assert a["키"] == "GC000272" and b["키"] == "GC001199"
|
|
assert a["값"]["상각비계수"] == 2000 and a["값"]["정비비계수"] == 0
|
|
docs = {a["키"]: a["문서"], b["키"]: b["문서"]}
|
|
targets = [
|
|
{"식": a["키"], "처음": easy, "개인": True},
|
|
{"식": a["키"], "처음": easy, "개인": True, "덮음": {"값": {"상각비계수": 2500}}},
|
|
{"식": b["키"], "처음": hour, "개인": True},
|
|
{"식": b["키"], "처음": hour, "개인": True, "덮음": {"값": {"경비": 10}}},
|
|
]
|
|
got = _node({"문서들": docs, "대상": targets})
|
|
assert got[0]["덮음"] == {} and got[0]["줄"] == easy
|
|
assert got[1]["줄"]["값"] == {**easy["값"], "손료계수": 2500}
|
|
assert got[2]["덮음"] == {} and got[2]["줄"] == hour
|
|
assert got[3]["줄"]["시간당"] == {"노무비": 1, "재료비": 2, "경비": 10}
|
|
|
|
|
|
# ── ⑤ M01 같은 이름 막음 ───────────────────────────────────────────────
|
|
def test_M01_같은_이름_막음(world):
|
|
dig = next(r for r in world["rows"] if r["키"] == DIG)
|
|
with store.at(store.FOLDER):
|
|
with pytest.raises(store.StoreError) as e:
|
|
mym.not_m01({"이름": dig["이름"], "규격": dig["규격"]})
|
|
assert e.value.status == 409
|
|
mym.not_m01({"이름": "sub3임시 없는 기계", "규격": "1"})
|
|
|
|
|
|
def test_서버_덮음_몸_틀림_400():
|
|
with store.at(store.FOLDER), pytest.raises(m01.StoreError) as e:
|
|
ms.sheet({"원문번호": "0201-0100", "값": {}}, None, {"값": {"가격": -1}})
|
|
assert e.value.status == 400
|
|
|
|
|
|
def test_수_꼴():
|
|
assert ms._plain(D("1.50")) == 1.5 and ms._plain(D("3")) == 3
|