Merge branch 'sub_laptop_2' of C:/Aislo_wt/sub_laptop_2 into main_laptop_1

This commit is contained in:
2026-11-15 14:27:41 +09:00
6 changed files with 264 additions and 2 deletions
+6 -1
View File
@@ -27,6 +27,7 @@ import {
} from "../common_util/common_util_formula_doc_cover";
import { subInputs, type StackDoc } from "../common_util/common_util_line_stack";
import type { Picked } from "../common_util/common_util_line_stack_pick";
import { machineCellName } from "../common_util/common_util_machine_symbol_name";
import { openFindPicker } from "../M02_MasterTemplete/M02_MasterTemplete_Find_Pick";
import { openStackPick } from "./line_stack_pick";
import { lt } from "./line_stack_text";
@@ -146,7 +147,11 @@ export function buildMachineCtl(h: CtlHooks, link: Link | undefined): HTMLElemen
: numBox(col, now, UNIT[col] ?? "", ro, setVal);
const row = el("div", {
className: "lstk-frow",
children: [el("b", { className: "lstk-sym", text: col }), "=", box],
children: [
el("b", { className: "lstk-sym", text: machineCellName(col), attrs: { title: col } }),
"=",
box,
],
});
if (mine !== undefined) {
const tag = createBadge(lt("Ctl_Changed"), "info", { size: 18 });
+1
View File
@@ -112,6 +112,7 @@ export interface FormulaDoc {
키?: string;
이름?: string;
결과단위?: string;
부품?: { 갈래?: string };
변수?: DocVar[];
식?: { 기호?: string; 식?: string }[];
결과?: string;
+2 -1
View File
@@ -26,6 +26,7 @@ import {
type MoneyKind,
type StackItem,
} from "./common_util_line_stack";
import { machineAlias } from "./common_util_machine_symbol_name";
type Row = Record<string, unknown>;
@@ -239,7 +240,7 @@ export function tableAlias(doc: FormulaDoc): Record<string, string> {
break;
}
}
return out;
return { ...out, ...machineAlias(doc) }; // 기계경비식 = 짧은 한글 이름(839-1 · 사전이 표 값 이름보다 먼저)
}
/** 식 글의 기호를 화면 이름으로(또는 `back` = 화면 이름을 기호로) */
@@ -0,0 +1,122 @@
/* =============================================================================
* common_util_machine_symbol_name.ts
* 기계경비식(부품 갈래) 기호 → 짧은 한글 이름(PLAN 839-1 · 요구 858 안 2) — 그림 전용 사전
* 「P_기계」 → 「가격」 · 「k_기계상각비」 → 「상각」 · 「C_손료」 → 「손료」 · 「r_기계잡재료」 → 「잡품」
* 화면 글만 바꿈 — 문서 자료 · 식 글 · 열쇠 · 계산 불변 · 식 글 고침 칸은 `tableAlias` 길이 이름 ↔ 기호로 오감
* 기계경비식 밖 문서 · 사용자가 손으로 바꾼 기호(사전에 없는 꼴)는 그대로 · 이름이 겹치면 그 기호만 그대로
* 화면 없음(시험이 Node 로 돌림) · 기계 제어 구역(836-1)도 `machineAlias` 로 같은 이름
* ========================================================================== */
import type { FormulaDoc } from "./common_util_formula_doc";
import { symbols } from "./common_util_line_stack";
/** 기호 → 이름(품셈 말 줄임 — 가격 · 상각 · 정비 · 관리 · 손료 · 가산 · 잡품) */
const NAME: Record<string, string> = {
C_손료: "손료",
C_브레이커: "브레이커손료",
C_집게: "집게손료",
P_기계: "가격",
P_기계전력: "전력",
P_기계주연료: "주연료",
P_덤프트럭: "가격",
P_덤프트럭2: "주연료",
P_브레이커: "브레이커가격",
P_집게: "집게가격",
P_전력: "전력단가",
P_자동차용경유: "경유",
P_휘발유: "휘발유",
P_체인오일: "체인오일",
P_체인톱손료: "체인톱손료",
P_MO연료지역: "연료단가",
P_LB직종: "조종원노임",
Q_기계조종원: "조종원",
Q_덤프트럭: "조종원",
Q_시간환산: "환산",
"Q_분자^표": "환산분자",
"Q_분모^표": "환산분모",
"Q_주연료^표": "휘발유량",
"Q_주연료2^표": "오일량",
"Q_잡^표": "잡품",
S_기계: "기계",
S_지역: "지역",
S_보정작업: "보정작업",
S_보정기종: "보정기종",
S_브레이커: "브레이커",
S_집게: "집게",
S_연료: "연료",
S_직종: "직종",
S_조종원직종: "조종원직종",
S_전력계약종별: "전력계약",
k_기계상각비: "상각",
k_기계정비비: "정비",
k_기계관리비: "관리",
k_기계손료: "계수",
k_덤프트럭: "상각",
k_덤프트럭2: "정비",
k_덤프트럭3: "관리",
k_브레이커상각: "브레이커상각",
k_브레이커정비: "브레이커정비",
k_브레이커관리: "브레이커관리",
k_집게상각비: "집게상각",
k_집게정비비: "집게정비",
k_집게관리비: "집게관리",
k_체인톱: "체인톱계수",
r_가산: "가산",
"r_가산^표": "가산표",
r_기계잡재료: "잡품",
r_덤프트럭: "잡품",
r_비율: "연료비율",
r_조합잡재료: "조합잡품",
w_기계운전사: "운전사",
w_일반기계운전: "운전사",
w_화물차운전사: "운전사",
};
/** 기계 한 줄 칸 이름 → 화면 이름(기계 제어 구역 값 칸 이름표) */
const CELL: Record<string, string> = {
가격: "가격",
상각비계수: "상각",
정비비계수: "정비",
관리비계수: "관리",
주연료종류: "연료",
손료계수: "계수",
주연료: "주연료",
전력: "전력",
조종원: "조종원",
잡재료율: "잡품",
노무비: "시간노무",
재료비: "시간재료",
경비: "시간경비",
};
/** 「P_EQ기계…」 차례 이름 — 그 기호 개수로 문서 꼴을 앎(셋 = 시간당 값 · 다섯 = 연료 · 전력 · 여섯 = 조종원도) · 칸 연결이 직접 입력으로 바뀌어도 같음 */
const SLOTS: Record<number, string[]> = {
3: ["시간노무", "시간재료", "시간경비"],
5: ["가격", "계수", "주연료", "전력", "잡품"],
6: ["가격", "계수", "주연료", "전력", "조종원", "잡품"],
};
/** 기계경비식 문서의 기호 → 화면 이름(이름 없는 · 겹치는 기호는 빠짐) */
export function machineAlias(doc: FormulaDoc): Record<string, string> {
if (doc.부품?.갈래 !== "기계경비식") return {};
const ids = symbols(doc);
const slot = SLOTS[ids.filter((x) => x.startsWith("P_EQ기계")).length] ?? [];
const want = (id: string): string | undefined =>
NAME[id] ?? (id.startsWith("P_EQ기계") ? slot[Number(id.slice(6) || 1) - 1] : undefined);
const count = new Map<string, number>();
const plain = new Set(ids);
for (const id of ids) {
const n = want(id);
if (n) count.set(n, (count.get(n) ?? 0) + 1);
}
const out: Record<string, string> = {};
for (const id of ids) {
const n = want(id);
// 겹침(같은 이름 둘 · 안 바뀌는 다른 기호가 이미 그 이름) = 그 기호는 그대로
if (n && count.get(n) === 1 && !(plain.has(n) && !want(n))) out[id] = n;
}
return out;
}
/** 기계 칸 이름(상각비계수 …) → 짧은 이름 · 없으면 그대로 — 기계 제어 구역(836-1) 값 칸 이름표 */
export const machineCellName = (col: string): string => CELL[col] ?? col;
@@ -247,6 +247,8 @@ def test_표값_화면이름_전수():
assert not g["틀림"], (k, g["틀림"][:3])
alias = g["별칭"]
assert len(set(alias.values())) == len(alias), k # 겹치면 2 · 3
if (docs[k].get("부품") or {}).get("갈래") == "기계경비식":
continue # 기계경비식 = 짧은 한글 이름(839-1 · 시험 test_839_1)
assert all("^표" not in v and k.endswith("^표") for k, v in alias.items()), k
named += len(alias)
assert named > 500
@@ -0,0 +1,131 @@
# -*- coding: utf-8 -*-
"""PLAN 839-1(요구 858 안 2) — 기계경비식 16 문서 기호를 짧은 한글 이름으로 그림(그림만 · 자료 0).
① 16 문서 · 기호 64 꼴 전부 이름 있음(이름 없는 기호 0) · 문서 안 이름 겹침 0 · 안 바뀐 기호와도 겹침 0
② 식 글 · 금액 글 왕복(보이는 이름 → 기호 = 처음 식과 같은 나무) · 보이는 글에 옛 기호 안 남음
③ 기계경비식 밖 문서 = 사전 안 탐 · 자료 json 안 바뀜(기호 · 식 · 열쇠 그대로 — 화면 길이 문서를 안 고침)
④ 사용자가 손으로 바꾼 기호 = 사전에 없는 꼴이라 그대로
"""
from __future__ import annotations
import copy
from M02_MasterTemplete import M02_Doc_Copy as dc
from resources.tester.test_804_2_line_stack import _ts
MACHINE = "기계경비식"
def _ids(doc: dict) -> list[str]:
out = []
for v in doc.get("변수", []):
out.append(
v["기호"]
+ (f"_{v['아래']}" if v.get("아래") else "")
+ (f"^{v['위']}" if v.get("위") else "")
)
for line in doc.get("식", []):
if line["기호"] not in out:
out.append(line["기호"])
return out
def _machines() -> dict[str, dict]:
docs = dc.folder()
got = {k: d for k, d in docs.items() if (d.get("부품") or {}).get("갈래") == MACHINE}
assert len(got) == 16
return got
def test_16_문서_이름_전수_겹침_없음():
docs = _machines()
keys = sorted(docs)
got = _ts("별칭", [{"문서": docs[k]} for k in keys])
forms: set[str] = set()
for k, g in zip(keys, got):
alias = g["별칭"]
ids = _ids(docs[k])
miss = [i for i in ids if i not in alias]
assert not miss, (k, miss) # 이름 없는 기호 0
assert len(set(alias.values())) == len(alias), (k, alias) # 겹침 0
assert not set(alias.values()) & set(ids), (k, alias) # 옛 기호와도 안 겹침
assert all("_" not in v and "^" not in v for v in alias.values()), (k, alias)
forms |= {i for i in ids if not i.startswith("P_EQ기계")}
assert not g["틀림"], (k, g["틀림"][:3]) # 식 글 왕복 = 같은 식
assert len(forms) == 58 # 64 꼴 = 사전 58 + 「P_EQ기계~6」 읽는 칸 이름 6
def test_대표_이름():
docs = _machines()
got = _ts("별칭", [{"문서": docs["GC000267"]}])[0]["별칭"]
assert got["P_기계"] == "가격"
assert [got[x] for x in ("k_기계상각비", "k_기계정비비", "k_기계관리비")] == [
"상각",
"정비",
"관리",
]
assert got["C_손료"] == "손료" and got["r_가산"] == "가산" and got["r_기계잡재료"] == "잡품"
assert got["P_기계주연료"] == "주연료" and got["Q_기계조종원"] == "조종원"
assert got["Q_시간환산"] == "환산" and got["Q_분자^표"] == "환산분자"
one = _ts("별칭", [{"문서": docs["GC001199"]}])[0]["별칭"]
assert [one[x] for x in ("P_EQ기계", "P_EQ기계2", "P_EQ기계3")] == [
"시간노무",
"시간재료",
"시간경비",
]
def test_밖_문서_그대로_자료_안_바뀜():
docs = dc.folder()
outside = [k for k, d in docs.items() if (d.get("부품") or {}).get("갈래") != MACHINE]
pick = [k for k in sorted(outside) if k in ("GC001147", "GC000089")] or sorted(outside)[:2]
before = [copy.deepcopy(docs[k]) for k in pick]
got = _ts("별칭", [{"문서": docs[k]} for k in pick])
for g in got:
assert not set(g["별칭"].values()) & {"가격", "상각", "정비", "관리", "손료", "잡품"}
assert [docs[k] for k in pick] == before # 문서 자료 안 바뀜
machine = _machines()
snap = copy.deepcopy(machine)
_ts("별칭", [{"문서": d} for d in machine.values()])
assert machine == snap
def test_손으로_바꾼_기호는_그대로():
docs = _machines()
doc = copy.deepcopy(docs["GC000267"])
for v in doc["변수"]:
if v["기호"] == "P" and v["아래"] == "기계":
v["아래"] = "차량" # 사용자가 손으로 고친 기호 = P_차량
alias = _ts("별칭", [{"문서": doc}])[0]["별칭"]
assert "P_차량" not in alias # 그림 사전 안 탐 = 그 글 그대로
def test_칸_읽는_기호_차례_이름_칸과_같음():
"""「P_EQ기계…」 이름 = 문서 모양 차례 — 읽는 칸(연결 칸)과 같고, 직접 입력으로 바뀌어도 안 변함."""
want = {
"가격": "가격",
"손료계수": "계수",
"주연료": "주연료",
"전력": "전력",
"조종원": "조종원",
"잡재료율": "잡품",
"노무비": "시간노무",
"재료비": "시간재료",
"경비": "시간경비",
}
docs = _machines()
for k in ("GC001199", "GC001200", "GC001201"):
doc = copy.deepcopy(docs[k])
eq = {
f"P_{v['아래']}": want[v["연결"]["칸"]]
for v in doc["변수"]
if v["기호"] == "P" and v["아래"].startswith("EQ기계")
}
got = _ts("별칭", [{"문서": doc}])[0]["별칭"]
assert {i: got[i] for i in eq} == eq, k
for v in doc["변수"]:
if v["아래"].startswith("EQ기계"):
v["연결"] = {"갈래": "숫자", "값": 7} # 직접 입력 = 칸 연결 없음
typed = _ts("별칭", [{"문서": doc}])[0]["별칭"]
assert {i: typed[i] for i in eq} == eq, k