feat(748-0): 단가산출 둘째 기초 — 세부 품 M01 원문 · 머리 수량 칸 하나 · 되돌림 셋(기초 빼기 · 더한 항목 빼기 · 더한 항목 수량)
- 둘째 기초 세부 줄 품 칸 = 서버 `원문`(0.871 그대로) · 금액 · 비목은 상자 합 몫 그대로 · 세부 고침도 원문 몫(머리 수량 · 화면배 안 나눔) - 더한 기초 머리 상자(새 `Sangeun_BaseHead`) — 기초마다 이름 · 수량(기준단위 몇 개) 칸 · [되돌림] · [기초 빼기] - 새 `logic/M02_Sangeun_Undo` — 뺀더한줄 기억 · 되살림(열쇠 당김 · 밂) · 처음수량 되돌림 · 넣을 때 처음수량 적음 - 되살림 띠에 뺀 더한줄도 · 일위대가 줄 패치 뺀더한줄 이음(RowSk · LinePatch) - 낡은 시험 고침: test_727 세부 고침 ÷수량 기대 → 원문 그대로 · test_717_2b 모델 인라인에 Undo 추가 - 새 시험 test_748_0_sk_undo(거울 helper_748_0)
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
/* PLAN 748-0 헬퍼 — SK 고침 꼴(`logic/M02_Sangeun_Cover_Model.ts` · `M02_Sangeun_Undo.ts`):
|
||||
* 둘째 기초 세부 품 = M01 원문 · 고침 = 원문 몫 그대로 · 빼기 기억(뺀더한줄) · 되살림 · 수량 처음값 되돌림.
|
||||
* 짝 = `test_748_0_sk_undo.py`. 결과 = 표준출력 JSON.
|
||||
*/
|
||||
const path = require("path");
|
||||
const { makeLoader } = require("./helper_ts_stub_loader.cjs");
|
||||
|
||||
const { load } = makeLoader({ root: path.join(__dirname, "..", "..") });
|
||||
const m = load("M02_MasterTemplete", "logic", "M02_Sangeun_Cover_Model.ts");
|
||||
|
||||
// 둘째 기초 GF 한 줄 — 원문 0.871 · 머리 수량 0.01 이면 상자 몫 0.00871
|
||||
const detail = {
|
||||
cid: "add:#1/labor:LB000101#0",
|
||||
이름: "벌목부",
|
||||
단위: "인",
|
||||
수량: 0.00871,
|
||||
단가: 100000,
|
||||
금액: 871,
|
||||
비목: { 노무비: 871 },
|
||||
원문: { 수량: 0.871, 금액: 87100, 비목: { 노무비: 87100 } },
|
||||
바탕값: { qty: 0.871, 단가: 100000 },
|
||||
};
|
||||
const ans = {
|
||||
기준: { 계: 871 },
|
||||
줄: [
|
||||
{
|
||||
cid: "labor:LB000002#0",
|
||||
이름: "보통인부",
|
||||
단위: "인",
|
||||
수량: 1,
|
||||
단가: 100,
|
||||
금액: 100,
|
||||
비목: { 노무비: 100 },
|
||||
},
|
||||
{
|
||||
cid: "add:#0",
|
||||
이름: "추가 인력",
|
||||
단위: "인",
|
||||
수량: 2,
|
||||
단가: 100,
|
||||
금액: 200,
|
||||
비목: { 노무비: 200 },
|
||||
},
|
||||
{
|
||||
cid: "add:#1",
|
||||
이름: "둘째 기초",
|
||||
단위: "100㎥",
|
||||
수량: 0.01,
|
||||
단가: null,
|
||||
금액: 871,
|
||||
비목: { 노무비: 871 },
|
||||
펼침: [detail],
|
||||
},
|
||||
],
|
||||
정체: ["labor:LB000002#0", "add:#0", "add:#1"],
|
||||
짝잃음: [],
|
||||
바탕판: "",
|
||||
};
|
||||
const doc = {
|
||||
단위: "㎥",
|
||||
바탕: { 로직: "GF000099" },
|
||||
더한줄: [
|
||||
{ 종류: "인력", 요소: "LB000002", 수량: 2, 처음수량: 2 },
|
||||
{ 종류: "로직", 로직: "GF000316", 수량: 0.01, 처음수량: 0.01 },
|
||||
],
|
||||
고름: { "add:#1": "MT1" },
|
||||
};
|
||||
const out = {};
|
||||
const rows = m.rowsOf(ans, null, doc);
|
||||
const sub = rows.find((r) => r.cid === detail.cid);
|
||||
const view = m.hoView(ans, null, null, doc);
|
||||
const at = view.cids.indexOf(detail.cid);
|
||||
out.shownQty = view.호표[at].수량; // 원문 그대로
|
||||
out.lineQty = view.lines[at].수량;
|
||||
out.lineAmount = view.lines[at].금액; // 상자 몫 그대로
|
||||
out.t = sub.t;
|
||||
// 세부 품 고침 — 화면 0.9 = 문서 덮개 0.9(머리 수량 안 나눔)
|
||||
m.setLineCover(doc, sub, { qty: 0.9 }, false);
|
||||
out.cover = doc.더한줄[1].덮개;
|
||||
out.coverShown = m.hoView(ans, null, null, doc).covers[at].qty;
|
||||
// 더한 항목 수량 고침 → 처음값 되돌림
|
||||
const man = m.rowsOf(ans, null, doc).find((r) => r.cid === "add:#0");
|
||||
m.setLineCover(doc, man, { qty: 5 }, false);
|
||||
out.manQty = doc.더한줄[0].수량;
|
||||
m.setLineCover(doc, man, null, false);
|
||||
out.manBack = doc.더한줄[0].수량;
|
||||
// 더한 항목 빼기 → 기억 + 열쇠 당김 → 되살림
|
||||
m.dropLine(doc, man);
|
||||
out.afterDrop = { adds: doc.더한줄.length, gone: doc.뺀더한줄, pick: doc.고름 };
|
||||
out.goneRows = m.goneRows(doc);
|
||||
m.restoreLine(doc, "gone:0");
|
||||
out.afterBack = {
|
||||
adds: doc.더한줄.map((x) => x.요소 ?? x.로직),
|
||||
gone: doc.뺀더한줄 ?? null,
|
||||
pick: doc.고름,
|
||||
};
|
||||
// 둘째 기초 통째 빼기 → 되살림
|
||||
m.dropBase(doc, 1, "둘째 기초");
|
||||
out.baseGone = { adds: doc.더한줄.length, name: m.goneRows(doc)[0].이름, pick: doc.고름 };
|
||||
m.restoreLine(doc, "gone:0");
|
||||
out.baseBack = doc.더한줄.map((x) => x.요소 ?? x.로직);
|
||||
process.stdout.write(JSON.stringify(out));
|
||||
@@ -35,7 +35,8 @@ def _text(p: Path) -> str:
|
||||
def _node(body: str) -> object:
|
||||
born = _text(M02 / "logic" / "M02_Born_Tag.ts") # 740-2 — 모델이 쓰는 꼬리표 글(import 줄은 뺌)
|
||||
model = re.sub(r"^import .*$", "", _text(MODEL), flags=re.M)
|
||||
src = born + "\n" + model + "\n" + body
|
||||
undo = re.sub(r"^import .*$", "", _text(M02 / "logic" / "M02_Sangeun_Undo.ts"), flags=re.M)
|
||||
src = born + "\n" + undo + "\n" + model + "\n" + body
|
||||
out = subprocess.run(
|
||||
["node", "--experimental-strip-types", "--no-warnings",
|
||||
"--input-type=module-typescript", "-e", src],
|
||||
|
||||
@@ -209,6 +209,7 @@ def _model(ans, doc, script):
|
||||
return _node(
|
||||
[
|
||||
"M02_MasterTemplete/logic/M02_Born_Tag.ts",
|
||||
"M02_MasterTemplete/logic/M02_Sangeun_Undo.ts",
|
||||
"M02_MasterTemplete/logic/M02_Sangeun_Cover_Model.ts",
|
||||
],
|
||||
body,
|
||||
@@ -242,7 +243,9 @@ console.log(JSON.stringify({
|
||||
assert "add:#0" not in got["cids"] and "add:#0/labor:LB000036#0" in got["cids"]
|
||||
assert got["sub"]["sub"] == {"k": 0, "cid": "labor:LB000036#0", "scale": 2}
|
||||
assert got["sub"]["수량"] == pytest.approx(0.036) and got["sub"]["묶음"] == "인력"
|
||||
assert got["covered"]["덮개"]["labor:LB000036#0"]["qty"] == pytest.approx(0.036) # ÷ 수량 2
|
||||
assert got["covered"]["덮개"]["labor:LB000036#0"]["qty"] == pytest.approx(
|
||||
0.072
|
||||
) # 화면 품 그대로(M01 원문 몫 · 748-0 — 수량 2 로 안 나눔)
|
||||
assert got["covered"]["덮개"]["labor:LB000036#0"]["원값"] == {"qty": 0.018, "단가": 233250}
|
||||
assert got["dropped"] == DROP and got["back"] is None
|
||||
assert got["tags"] == ["9-12-1", "9-12-1", "3-2-1", "3-2-1"] # 바탕 둘 · 둘째 기초 인력 둘
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
"""748-0 지킴 — SK 둘째 기초 세부 품 = M01 원문(머리 수량 안 곱함) · 고침 꼴 · 빼기 기억 · 되살림 · 수량 처음값.
|
||||
(서버 짝 = `test_744_1_sk_detail_undo.py`)"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
|
||||
|
||||
def test_세부_원문_품_고침_빼기_되살림_처음값():
|
||||
proc = subprocess.run( # noqa: S603 — 고정 실행 파일
|
||||
["node", str(HERE / "helper_748_0_sk_undo.cjs")],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
timeout=60,
|
||||
)
|
||||
assert proc.returncode == 0, proc.stderr
|
||||
out = json.loads(proc.stdout)
|
||||
assert float(out["shownQty"]) == pytest.approx(0.871) # 곱하지 않은 M01 원문
|
||||
assert out["lineQty"] == pytest.approx(0.871)
|
||||
assert out["lineAmount"] == pytest.approx(871) # 상자 몫은 그대로
|
||||
assert out["t"] == 1
|
||||
assert out["cover"]["labor:LB000101#0"]["qty"] == pytest.approx(0.9) # 머리 수량 안 나눔
|
||||
assert out["coverShown"] == pytest.approx(0.9) # 다시 그려도 같음
|
||||
assert out["manQty"] == 5 and out["manBack"] == 2 # [되돌림] = 넣을 때 수량
|
||||
drop = out["afterDrop"]
|
||||
assert drop["adds"] == 1 and drop["gone"][0]["차례"] == 0
|
||||
assert drop["pick"] == {"add:#0": "MT1"} # 뒤 더한줄 열쇠 당김
|
||||
assert out["goneRows"][0]["cid"] == "gone:0"
|
||||
back = out["afterBack"]
|
||||
assert back["adds"] == ["LB000002", "GF000316"] and back["gone"] is None
|
||||
assert back["pick"] == {"add:#1": "MT1"} # 되살림 = 밂
|
||||
assert out["baseGone"] == {"adds": 1, "name": "둘째 기초", "pick": {}}
|
||||
assert out["baseBack"] == ["LB000002", "GF000316"]
|
||||
Reference in New Issue
Block a user