Merge remote-tracking branch 'origin/sub_laptop_5' into main_laptop_1
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
"""흐름 로직 왼쪽 「단가산출 상세」 줄 만들기(`M01_MasterData_UI_LogicLab_FlowRows.ts`) — 순수 부분.
|
||||
|
||||
화면 TS 를 그 자리에서 트랜스파일해 node 로 돌림 — 상대 경로 TS(DataFlow Round …)는 이어 트랜스파일 ·
|
||||
화면 부품(@ui · @config)은 빈 흉내. 견본 로직 · 계산 결과는 이 시험 안에 둠(마스터 내용에 기대지 않음).
|
||||
"""
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
SOURCE = ROOT / "M01_MasterData" / "M01_MasterData_UI_LogicLab_FlowRows.ts"
|
||||
|
||||
RUNNER = r"""
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const [root, file] = process.argv.slice(1);
|
||||
const ts = require(path.join(root, "config", "node_modules", "typescript"));
|
||||
const stub = new Proxy({}, { get: (_t, k) => (k === "currentLanguageIndex" ? 0 : k === "API_BASE_URL" ? "http://127.0.0.1:1/api" : () => undefined) });
|
||||
const cache = {};
|
||||
function load(f) {
|
||||
if (cache[f]) return cache[f].exports;
|
||||
const js = ts.transpileModule(fs.readFileSync(f, "utf8"), {
|
||||
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
|
||||
}).outputText;
|
||||
const box = (cache[f] = { exports: {} });
|
||||
const req = (id) => {
|
||||
if (id.startsWith(".")) {
|
||||
const p = path.resolve(path.dirname(f), id);
|
||||
return fs.existsSync(p + ".ts") ? load(p + ".ts") : stub;
|
||||
}
|
||||
return stub;
|
||||
};
|
||||
new Function("exports", "module", "require", js)(box.exports, box, req);
|
||||
return box.exports;
|
||||
}
|
||||
const m = load(file);
|
||||
const jobs = JSON.parse(fs.readFileSync(0, "utf8"));
|
||||
const out = jobs.map((job) => m.flowRows(job.lg, job.ev, job.round));
|
||||
process.stdout.write(JSON.stringify(out));
|
||||
setTimeout(() => process.exit(0), 50);
|
||||
"""
|
||||
|
||||
ROUND = {"item": {"places": 1, "method": "버림"}, "total": {"places": 0, "method": "버림"}}
|
||||
|
||||
|
||||
def box(id_, kind, name, unit="원", **extra):
|
||||
return {
|
||||
"id": id_,
|
||||
"kind": kind,
|
||||
"name": name,
|
||||
"color": "#000",
|
||||
"unit": unit,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
**extra,
|
||||
}
|
||||
|
||||
|
||||
def out_box(id_, name, cost, unit="원", round_=None):
|
||||
output = {"cost": cost, "unit": unit}
|
||||
if round_:
|
||||
output["round"] = round_
|
||||
return box(id_, "output", name, unit, output=output)
|
||||
|
||||
|
||||
def func_box(id_, name, out_name, unit="시간"):
|
||||
func = {
|
||||
"sheet": {"id": "s-" + id_, "이름": name, "칸": {}},
|
||||
"inputs": [],
|
||||
"variables": [],
|
||||
"output": {"cell": "B1", "name": out_name, "unit": unit},
|
||||
}
|
||||
return box(id_, "function", name, unit, func=func)
|
||||
|
||||
|
||||
def logic(boxes):
|
||||
return {
|
||||
"key": None,
|
||||
"name": "시험",
|
||||
"resultUnit": "원/㎡",
|
||||
"boxes": boxes,
|
||||
"links": [],
|
||||
"userElements": [],
|
||||
}
|
||||
|
||||
|
||||
def result(outputs, cost, ports=None, errors=None):
|
||||
return {"ports": ports or {}, "outputs": outputs, "cost": cost, "errors": errors or []}
|
||||
|
||||
|
||||
def run(jobs: list[dict]) -> list:
|
||||
node = "node"
|
||||
done = subprocess.run(
|
||||
[node, "-e", RUNNER, str(ROOT), str(SOURCE)],
|
||||
input=json.dumps(jobs),
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=60,
|
||||
check=False,
|
||||
)
|
||||
assert done.returncode == 0, done.stderr
|
||||
return json.loads(done.stdout)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def sample():
|
||||
lg = logic(
|
||||
[
|
||||
out_box("o1", "인건비", "노무비"),
|
||||
out_box("o2", "재료값", "재료비"),
|
||||
out_box("o3", "시간당 작업량", "값", "㎥/시간"),
|
||||
out_box("o4", "특별 끝수", "경비", round_={"places": 2, "method": "반올림"}),
|
||||
func_box("f1", "운반 시간", "시간", "시간"),
|
||||
]
|
||||
)
|
||||
ev = result(
|
||||
{"o1": 3216.69, "o2": 100.04, "o3": 12.5, "o4": 1.239},
|
||||
{
|
||||
"노무비": 3216.69,
|
||||
"재료비": 100.04,
|
||||
"경비": 1.239,
|
||||
"값들": {"시간당 작업량": 12.5},
|
||||
"계": 3317.969,
|
||||
},
|
||||
ports={"f1": {"시간": 6.024}},
|
||||
)
|
||||
return run([{"lg": lg, "ev": ev, "round": ROUND}])[0]
|
||||
|
||||
|
||||
def group(rows, key):
|
||||
return next(g for g in rows["groups"] if g["key"] == key)
|
||||
|
||||
|
||||
def test_group_order_and_costs_only(sample):
|
||||
assert [g["key"] for g in sample["groups"]] == ["labor", "material", "cost", "plain"]
|
||||
|
||||
|
||||
def test_line_uses_output_round(sample):
|
||||
# 출력 끝수를 안 정한 비용 줄 = 기본 0.1원 버림 → 3,216.6 (정수로 반올림하지 않음)
|
||||
assert group(sample, "labor")["lines"][0]["text"] == "3,216.6"
|
||||
assert group(sample, "material")["lines"][0]["text"] == "100"
|
||||
# 그 출력의 끝수가 있으면 그 자리대로(소수 둘째 반올림)
|
||||
assert group(sample, "cost")["lines"][0]["text"] == "1.24"
|
||||
|
||||
|
||||
def test_plain_value_is_not_rounded_and_has_no_subtotal(sample):
|
||||
plain = group(sample, "plain")
|
||||
assert plain["lines"][0]["text"] == "12.5"
|
||||
assert plain["lines"][0]["unit"] == "㎥/시간"
|
||||
assert plain["sub"] is None
|
||||
|
||||
|
||||
def test_subtotal_and_total_floor_to_won(sample):
|
||||
assert group(sample, "labor")["sub"] == 3216
|
||||
assert group(sample, "labor")["subText"] == "3,216"
|
||||
assert sample["total"] == 3317
|
||||
assert sample["totalText"] == "3,317"
|
||||
|
||||
|
||||
def test_function_rows(sample):
|
||||
assert sample["funcs"] == [
|
||||
{"box": "f1", "name": "운반 시간", "unit": "시간", "value": 6.024, "text": "6.024"}
|
||||
]
|
||||
|
||||
|
||||
def test_empty_group_is_hidden():
|
||||
lg = logic([out_box("o1", "인건비", "노무비")])
|
||||
ev = result({"o1": 10}, {"노무비": 10, "재료비": 0, "경비": 0, "값들": {}, "계": 10})
|
||||
rows = run([{"lg": lg, "ev": ev, "round": ROUND}])[0]
|
||||
assert [g["key"] for g in rows["groups"]] == ["labor"]
|
||||
assert rows["total"] == 10
|
||||
|
||||
|
||||
def test_missing_value_shows_dash():
|
||||
lg = logic([out_box("o1", "인건비", "노무비")])
|
||||
ev = result(
|
||||
{"o1": None}, {"노무비": 0, "재료비": 0, "경비": 0, "값들": {}, "계": 0}, errors=["끊김"]
|
||||
)
|
||||
rows = run([{"lg": lg, "ev": ev, "round": ROUND}])[0]
|
||||
assert group(rows, "labor")["lines"][0]["text"] == "—"
|
||||
assert rows["errors"] == ["끊김"]
|
||||
Reference in New Issue
Block a user