feat(736-2): 빠진 길 둘 — 찾아 넣기 창 [신규 자재](개인 층) · 기계 탭 · 엑셀 연결 줄 갈래대로 탭

- 개인 층 재료 고름 = 창 머리 [신규 자재] — 만들어 바로 자재 목록에 넣음(`openNewMaterial`)
- 찾아 넣기 창 기계(M01 EQ) 탭 — 「M01 전체」 묶음 안 · 부르는 쪽이 `machine` 을 줄 때만 보임
- 엑셀 연결 = 줄 갈래대로 탭(노임 = 인력 · 기계 = 기계 · 자재 = 재료 · 그 밖 = 기초) — 불러오기 고르기 · 남은 줄 · 못옮김 · 안 이은 줄 연결 모두 같은 창 · 따로 [기계] 단추 걷음
- 낡은 시험 고침(721 · 729-3: 기계 고름 길이 찾아 넣기 창으로 옮김) + 736-2 글 시험 추가

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-02 17:42:05 +09:00
co-authored by Claude Sonnet 5.5
parent f97ea96c08
commit bc479bb3d0
10 changed files with 141 additions and 103 deletions
@@ -12,13 +12,16 @@
* · (727-1) 기초 = 로직마다 한 줄(`logicHeads` · 옵션은 넣은 뒤 고름 · 옛 창도 같이) · 서버 목록 그대로
* · (736-1) `kinds` = 탭 막기(자리마다 고를 수 있는 종류만 · 불러오기 엑셀 연결 · 별칭 장부) · `material` = 재료 고름을
* 일위대가 「자재 목록」 자리로(`element` 대신) · `query` = 찾기 칸 미리 넣기 · 부르는 곳 = 일위대가 [항목 추가]
* · (736-2) `machine` = M01 기계(EQ · 기계 줄 이음) 고름 · 개인 층에서 재료 고름이면 머리 [신규 자재](개인 층에 만들어 바로 고름) ·
* 기계 줄은 「M01 전체」 탭 묶음 안(인력 · 재료와 같은 탭)
* · (728-2) 돈 아닌 기초(결과단위가 「원」 아님 · ㎥/시간 등 21 개) = 목록에서 뺌
* ========================================================================== */
import { createDataTable } from "@ui/ui_template_data_table";
import { createInputField, el, showToast } from "@ui/ui_template_elements";
import { createButton, createInputField, el, showToast } from "@ui/ui_template_elements";
import { t as L } from "@ui/ui_template_locale";
import { aliasNote, matchLogic } from "@ui/ui_template_logic_find";
import { openNewMaterial } from "./M02_Material_Pick";
import { openModal } from "@ui/ui_template_modal";
import { createTabs } from "@ui/ui_template_tabs";
import { createText } from "@ui/ui_template_text";
@@ -40,7 +43,7 @@ type Tab = "m01" | "sk" | "pre";
/** M01 인력 · 재료 한 줄(`/logic-lib/pick` · 코드 = LB- · MT- 키) */
export interface M01Element {
종류: "인력" | "재료";
종류: "인력" | "재료" | "기계";
코드: string;
이름: string;
규격: string;
@@ -48,8 +51,8 @@ export interface M01Element {
값: number | null;
}
/** 고를 수 있는 종류 — logic = M01 단가산출 · 기초 로직 · labor = 인력 · material = 재료 · sk = M02 단가산출 · pre = 일위대가 */
export type FindKind = "logic" | "labor" | "material" | "sk" | "pre";
/** 고를 수 있는 종류 — logic = M01 단가산출 · 기초 로직 · labor = 인력 · material = 재료 · machine = 기계 · sk = M02 단가산출 · pre = 일위대가 */
export type FindKind = "logic" | "labor" | "material" | "machine" | "sk" | "pre";
export interface FindPickOptions {
layer: Layer;
@@ -67,6 +70,8 @@ export interface FindPickOptions {
ref?: (ref: LaborRef, name: string) => void;
/** M01 인력 · 재료 고름 — 없으면 그 줄 회색 막힘 */
element?: (e: M01Element) => void;
/** 736-2 M01 기계(EQ) 고름 — 있을 때만 기계 줄이 보임 */
machine?: (e: M01Element) => void;
}
/** 727-1 기초마다 한 줄(옛 창 `Labor_Pick` 도 씀) — 규격 칸 = 「옵션 N」 */
@@ -131,7 +136,7 @@ export function openFindPicker(opt: FindPickOptions): void {
const ok = (k: FindKind): boolean => !opt.kinds || opt.kinds.includes(k);
const allowed = (
[
["m01", ok("logic") || ok("labor") || ok("material")],
["m01", ok("logic") || ok("labor") || ok("material") || (!!opt.machine && ok("machine"))],
["sk", ok("sk")],
["pre", ok("pre")],
] as const
@@ -144,6 +149,7 @@ export function openFindPicker(opt: FindPickOptions): void {
let std: StandardRow[] | null = null;
let people: PickItem[] = [];
let goods: PickItem[] = [];
let machines: PickItem[] = [];
const lists: Record<"sk" | "pre", SangeunRow[] | null> = { sk: null, pre: null };
let shown: Line[] = [];
let seq = 0;
@@ -203,7 +209,12 @@ export function openFindPicker(opt: FindPickOptions): void {
단위: unitOf(m.단위 ?? ""),
값: Number.isFinite(Number(m.값)) && m.값 !== null ? Number(m.값) : null,
};
const put = kind === "재료" && opt.material ? opt.material : opt.element;
const put =
kind === "기계"
? opt.machine
: kind === "재료" && opt.material
? opt.material
: opt.element;
return {
품명: m.이름,
규격: e.규격,
@@ -233,6 +244,7 @@ export function openFindPicker(opt: FindPickOptions): void {
...(ok("logic") ? logicLines(q) : []),
...(ok("labor") ? elementLines("인력", people) : []),
...(ok("material") ? elementLines("재료", goods) : []),
...(opt.machine && ok("machine") ? elementLines("기계", machines) : []),
];
} else {
const words = q.toLowerCase().split(/\s+/).filter(Boolean);
@@ -255,13 +267,18 @@ export function openFindPicker(opt: FindPickOptions): void {
call<{ items: PickItem[] }>(
`/logic-lib/pick?kind=${kind}&q=${encodeURIComponent(q)}&limit=200`,
).then((d) => d.items);
const getMachine = (): Promise<PickItem[]> =>
call<{ items: PickItem[] }>(
`/logic-lib/elements?group=${encodeURIComponent("기계")}&q=${encodeURIComponent(q)}&limit=200`,
).then((d) => d.items.map((x) => ({ ...x, 단위: "시간", 값: null }))); // 기계 단위 · 값 = 칸별 표(시간당 경비는 서버 셈)
try {
const [p, g] = await Promise.all([
const [p, g, m] = await Promise.all([
ok("labor") ? get("job") : Promise.resolve([]),
q && ok("material") ? get("price") : Promise.resolve([]),
opt.machine && ok("machine") ? getMachine() : Promise.resolve([]),
]);
if (mine !== seq) return;
[people, goods] = [p, g];
[people, goods, machines] = [p, g, m];
} catch (error) {
if (mine !== seq) return;
showToast(error instanceof Error ? error.message : tc("Load_Failed"), "error");
@@ -289,8 +306,29 @@ export function openFindPicker(opt: FindPickOptions): void {
},
});
// 736-2 개인 층 + 재료 고름 = 머리 [신규 자재] — 만들어 바로 고름(우리 M01 에 없는 자재)
const newMat = createButton({
label: L("M02_FindNewMaterial"),
variant: "ghost",
onClick: () =>
openNewMaterial(opt.owner, search.input.value.trim(), (row) =>
take(() =>
opt.material?.({
종류: "재료",
코드: row.키,
이름: row.이름,
규격: row.규격,
단위: row.단위,
값: row.값,
}),
)(),
),
});
newMat.dataset.materialNew = "";
const canNew = opt.layer === "personal" && !!opt.material && ok("material");
({ close } = openModal({
title: opt.title ?? L("M02_FindTitle"),
...(canNew ? { actions: [newMat] } : {}),
closeLabel: tc("Pick_Close"),
dialogClass: "m01-logic__pick m01-combo",
width: "960px", // 표 여섯 칸
@@ -170,6 +170,17 @@ export const candText = (c: HopyoTarget, twins: Set<string>): string =>
export const canPickMaterial = (line: Pick<HopyoLine, "갈래" | "단가">): boolean =>
MATERIAL.has(line.갈래) || !!line.단가?.재료비;
/** 736-2 [다른 항목 검색…] 탭 — 줄 갈래대로(서버 이음 칸 = 노임 LB · 기계 EQ · 자재 · 로직) */
export const kindsOf = (line: Pick<HopyoLine, "갈래" | "단가">): FindKind[] =>
line.갈래 === "노임"
? ["labor"]
: line.갈래 === "기계"
? ["machine"]
: [
...(MATERIAL.has(line.갈래) ? [] : (["logic"] as const)),
...(canPickMaterial(line) ? (["material"] as const) : []),
];
/** 고른 후보 → 만들기 대상(배 1 · 몫 있으면 배 = 몫) */
export const pickedTargets = (picked: { c: HopyoTarget; 몫: number | null }[]): BuildTarget[] =>
picked.map(({ c, 몫 }) => ({
@@ -294,11 +305,8 @@ export function openHopyoChoose(opt: ChooseOptions): void {
: []),
],
});
// 736-1 찾아 넣기 창 하나 — 이을 수 있는 종류만(자재 줄 = 재료 · 그 밖 = 기초 로직 · 재료비 있는 줄은 재료도)
const kinds: FindKind[] = [
...(material ? [] : (["logic"] as const)),
...(canPickMaterial(line) ? (["material"] as const) : []),
];
// 736-1 · 736-2 찾아 넣기 창 하나 — 줄 갈래대로 탭(노임 = 인력 · 기계 = 기계 · 자재 = 재료 · 그 밖 = 기초 로직 · 재료비 있는 줄은 재료도)
const kinds = kindsOf(line);
const find = createButton({
label: "다른 항목 검색…",
size: "sm",
@@ -308,9 +316,11 @@ export function openHopyoChoose(opt: ChooseOptions): void {
layer: getActiveLayer(),
owner: getActiveOwner(),
kinds,
query: searchHint(line),
query: searchHint(line.갈래 === "기계" ? { 이름: line.규격 || line.이름 } : line), // 기계 = 규격 낱말(굴착기)
logic: (x) => opt.done([{ 로직: x.키, 입력값: {}, 배: 1 }]),
material: (e) => opt.done([{ 자재: e.코드, 배: 1 }]),
element: (e) => opt.done([{ 노임: e.코드, 배: 1 }]),
machine: (e) => opt.done([{ 기계: e.코드, 배: 1 }]),
});
},
});
@@ -13,13 +13,11 @@
import { createBadge } from "@ui/ui_template_badge";
import { createBanner } from "@ui/ui_template_banner";
import { createFindList } from "@ui/ui_template_find_list";
import { createButton, createSelectField, el, showToast } from "@ui/ui_template_elements";
import { t as L } from "@ui/ui_template_locale";
import { openModal } from "@ui/ui_template_modal";
import { createTable, type Cell } from "@ui/ui_template_table";
import { createText } from "@ui/ui_template_text";
import { call } from "./M02_MasterTemplete_Api_Fetch";
import {
guessHopyo,
leftHopyo,
@@ -35,7 +33,7 @@ import {
type LinkBase,
} from "./M02_MasterTemplete_Hopyo_Api";
import { bodyOf, draftKey, loadDraft, saveDraft, toTarget } from "./M02_MasterTemplete_Hopyo_Check";
import { openHopyoChoose } from "./M02_MasterTemplete_Hopyo_Choose";
import { kindsOf, openHopyoChoose } from "./M02_MasterTemplete_Hopyo_Choose";
import { openFindPicker } from "./M02_MasterTemplete_Find_Pick";
import { openLost } from "./M02_MasterTemplete_Hopyo_Lost";
@@ -118,49 +116,6 @@ function pickedText(at: LeftScope, no: number, 차례: number): string {
.join(" + ");
}
/** M01 기계(EQ) 하나 고르기 — 이름 · 규격 낱말 */
export function pickMachine(
line: { 이름: string; 규격?: string },
done: (code: string, name: string) => void,
): void {
openModal({
title: `${L("M02_LeftMachine")} — ${line.이름} ${line.규격 ?? ""}`.trim(),
closeLabel: L("M02_Close"),
dialogClass: "m02-modal",
mount: (body, close) => {
const find = createFindList({ placeholder: L("M02_LeftMachineFind") });
let seq = 0;
const run = (q: string): void => {
const mine = ++seq;
call<{ total: number; items: { ref: string; 이름?: string; 규격?: string }[] }>(
`/logic-lib/elements?group=${encodeURIComponent("기계")}&q=${encodeURIComponent(q)}&limit=100`,
).then(
(got) => {
if (mine !== seq) return;
find.hint.textContent = `${got.total}`;
find.setRows(
got.items.map((x) => ({
key: x.ref,
name: `${x.이름 ?? ""} ${x.규격 ?? ""}`.trim(),
sub: x.ref,
onPick: () => {
close();
done(x.ref, `${x.이름 ?? ""} ${x.규격 ?? ""}`.trim());
},
})),
);
},
(error) => showToast(why(error), "error"),
);
};
find.search.addEventListener("input", () => run(find.search.value));
find.search.value = line.이름.split(/[\s(]/)[0] ?? "";
run(find.search.value);
body.append(find.root);
},
});
}
/** 남은 줄 창 — `map` 이 없으면 `nos` 를 서버에 물어 채움 · 닫을 때 `onClose(map)` */
export function openLeft(
at: LeftScope & { 파일: string; nos?: number[]; map?: LeftMap },
@@ -213,9 +168,11 @@ export function openLeft(
layer: at.target.layer,
owner: at.target.owner,
title: `${line.이름} ${line.규격}`.trim(),
kinds: kindsOf(line), // 736-2 줄 갈래대로 탭(노임 = 인력 · 기계 = 기계 · 자재 = 재료)
logic: (x) => link(no, line.차례, { 로직: x.키 }, x.이름),
element: (e) =>
link(no, line.차례, e.종류 === "인력" ? { 노임: e.코드 } : { 자재: e.코드 }, e.이름),
machine: (e) => link(no, line.차례, { 기계: e.코드 }, e.이름),
});
const makeSk = async (no: number, block: number, 로직: string): Promise<void> => {
try {
@@ -277,16 +234,6 @@ export function openLeft(
createButton({ label: L("M02_LeftChoose"), size: 24, variant: "ghost", onClick: () => choose(no, line) }),
createButton({ label: L("M02_LeftFind"), size: 24, variant: "ghost", onClick: () => find(no, line) }),
); // prettier-ignore
if (line && row.갈래 === "기계")
box.append(
createButton({
label: L("M02_LeftMachine"),
size: 24,
variant: "ghost",
onClick: () =>
pickMachine(line, (code, name) => link(no, row.차례, { 기계: code }, name)),
}),
);
const none = createButton({
label: NONE,
size: 24,
@@ -22,7 +22,6 @@ import {
type SangeunLink,
type SangeunLost,
} from "./M02_MasterTemplete_Hopyo_Api";
import { pickMachine } from "./M02_MasterTemplete_Hopyo_Left";
const why = (error: unknown): string => (error instanceof Error ? error.message : String(error));
const NONE = "우리에 없음";
@@ -120,18 +119,17 @@ export function openLost(opt: LostOptions): void {
size: 24,
variant: "ghost",
onClick: () =>
kind === "기계"
? pickMachine(x, (code) => put(x.id, { id: x.id, 기계: code }))
: openFindPicker({
layer: opt.scope.layer,
owner: opt.scope.owner,
title: `${x.이름} ${x.규격 ?? ""}`.trim(),
logic: () => showToast(L("M02_GridLinkNoLogic"), "info"),
element: (e) =>
e.종류 === (kind === "노임" ? "인력" : "재료")
? put(x.id, { id: x.id, [kind]: e.코드 })
: showToast(L("M02_LostKind").replace("{k}", kind), "warning"),
}),
// 736-2 줄 갈래대로 탭 하나(노임 = 인력 · 기계 = 기계 · 자재 = 재료) → 이음 칸 `노임` · `기계` · `자재`
openFindPicker({
layer: opt.scope.layer,
owner: opt.scope.owner,
title: `${x.이름} ${x.규격 ?? ""}`.trim(),
kinds: [kind === "노임" ? "labor" : kind === "기계" ? "machine" : "material"],
query: x.이름.split(/[\s(]/)[0] ?? "",
logic: () => showToast(L("M02_GridLinkNoLogic"), "info"),
element: (e) => put(x.id, { id: x.id, [kind]: e.코드 }),
machine: (e) => put(x.id, { id: x.id, 기계: e.코드 }),
}),
});
const drop = createButton({
label: NONE,
@@ -14,7 +14,6 @@ import { openModal } from "@ui/ui_template_modal";
import { createTable } from "@ui/ui_template_table";
import { getActiveLayer, getActiveOwner } from "./M02_MasterTemplete_Api_Fetch";
import { openFindPicker } from "./M02_MasterTemplete_Find_Pick";
import { pickMachine } from "./M02_MasterTemplete_Hopyo_Left";
import type { LaborDoc } from "./M02_MasterTemplete_Labor_Api";
import type { LaborFixed } from "./M02_MasterTemplete_Labor_Calc";
@@ -57,19 +56,14 @@ function openLink(doc: LaborDoc, changed: () => void): void {
title: `${fx.이름} ${fx.규격 ?? ""}`.trim(),
logic: () => showToast(L("M02_GridLinkNoLogic"), "info"),
element: (e) => done(e.종류 === "인력" ? "노임" : "자재", e.코드),
machine: (e) => done("기계", e.코드), // 736-2 기계도 같은 창 탭
}),
});
const machine = createButton({
label: L("M02_LeftMachine"),
size: 24,
variant: "ghost",
onClick: () => pickMachine(fx, (code) => done("기계", code)),
});
return [
fx.이름,
fx.규격 ?? "",
fx.단위 ?? "",
el("div", { className: "ui-toolbar", children: [find, machine] }),
el("div", { className: "ui-toolbar", children: [find] }),
];
});
body.replaceChildren(
@@ -84,6 +84,8 @@ export function openSangeunLinePicker(
...scope,
logic: (x) => done({ 종류: "로직", 로직: x.키, 입력값: {}, 수량: 1 }),
ref: (ref) => done({ 참조: ref, 수량: 1 }),
element: (e) => done({ 종류: e.종류, 요소: e.코드, 이름: e.이름, 단위: e.단위, 수량: 1 }),
element: (e) =>
e.종류 !== "기계" &&
done({ 종류: e.종류, 요소: e.코드, 이름: e.이름, 단위: e.단위, 수량: 1 }),
});
}
+31 -2
View File
@@ -4,6 +4,7 @@
* · 일위대가 [자재 추가] 단추(`addMaterialButton` · 148 에서 `Labor_Detail` 이 덜어 냄)
* · 개인 층(148-3) = 개인 자재(「개인」 표 · 앞) + M01 · 머리 [신규 자재] → 품명 · 단위(필수) · 규격 · 단가 →
* 서버가 품번(`PM` + 6자리)을 주고 그 줄로 고름(`M02_Material_Own`)
* · (736-2) `openNewMaterial` = 찾아 넣기 창(`Find_Pick`) 재료 탭 머리 [신규 자재] — 입력 창만 따로 열고 저장하면 그 줄을 돌려줌
* 모양(`m02-pick*`)은 `M02_MasterTemplete_Structure.css`.
* ========================================================================== */
@@ -25,7 +26,7 @@ export interface MaterialItem {
}
/** 개인 자재 한 줄(서버 `개인자재.json` 꼴) */
interface OwnRow {
export interface OwnRow {
키: string;
이름: string;
규격: string;
@@ -55,6 +56,7 @@ const TEXT = {
key: "품번 — 저장 때 자동",
save: "저장",
back: "목록으로",
cancel: "취소",
needName: "품명 · 단위는 꼭 적음",
};
@@ -163,12 +165,39 @@ export function pickMaterial(done: (item: MaterialItem) => void, own?: OwnLayer)
});
}
/** 736-2 신규 자재 입력 창 — [저장] = 개인 층에 만들고 그 줄로 `saved` · 창 닫음 */
export function openNewMaterial(
owner: string | null,
seed: string,
saved: (row: OwnRow) => void,
): void {
openModal({
title: TEXT.newMat,
closeLabel: TEXT.pickClose,
dialogClass: "m02-modal m02-pick",
mount: (body, close) =>
body.append(
newForm(
{ owner },
seed,
(row) => {
close();
saved(row);
},
close,
TEXT.cancel,
),
),
});
}
/** 신규 자재 입력(148-3) — [저장] = 서버 추가 → 그 줄로 `saved` · 실패는 서버 까닭 그대로 */
function newForm(
own: OwnLayer,
seed: string,
saved: (row: OwnRow) => void,
back: () => void,
backLabel: string = TEXT.back,
): HTMLElement {
const name = createInputField({ label: TEXT.name, value: seed, required: true });
const spec = createInputField({ label: TEXT.spec });
@@ -224,7 +253,7 @@ function newForm(
createText(TEXT.key),
el("div", {
className: "ui-btn-group",
children: [createButton({ label: TEXT.back, variant: "ghost", onClick: back }), save],
children: [createButton({ label: backLabel, variant: "ghost", onClick: back }), save],
}),
],
});
+25 -4
View File
@@ -57,7 +57,7 @@ def test_721_2_부르는_곳_셋만_새_창():
for key in ('key: "m01"', 'key: "sk"', 'key: "pre"'):
assert key in find
assert "createSelectField" not in find and "Hopyo" not in find
assert '(kind: "job" | "price")' in find and "/elements" not in find
assert '(kind: "job" | "price")' in find and "/elements" in find # 736-2 기계(EQ) 묶음
def test_721_2_고정_줄_꼴():
@@ -122,11 +122,32 @@ def test_736_1_항목_추가_찾기_창_하나():
pick = _text(M02 / "M02_MasterTemplete_Labor_Pick.ts")
assert "material: lib.material," in pick
find = _text(M02 / "M02_MasterTemplete_Find_Pick.ts")
assert 'kind === "재료" && opt.material ? opt.material : opt.element' in find
assert 'export type FindKind = "logic" | "labor" | "material" | "sk" | "pre";' in find
assert 'kind === "재료" && opt.material' in find and "? opt.material" in find
assert '"logic" | "labor" | "material" | "machine" | "sk" | "pre"' in find
# 불러오기 엑셀 연결 = 기초(자재 아닌 줄) · 재료(자재 · 재료비 줄) · 별칭 장부 = 기초 · 재료 둘
choose = _text(M02 / "M02_MasterTemplete_Hopyo_Choose.ts")
assert "다른 로직 검색" not in choose and "다른 자재 검색" not in choose
assert 'material ? [] : (["logic"] as const)' in choose
assert 'MATERIAL.has(line.갈래) ? [] : (["logic"] as const)' in choose
assert 'canPickMaterial(line) ? (["material"] as const) : []' in choose
assert 'kinds: ["logic", "material"],' in _text(M02 / "M02_MasterTemplete_Hopyo_Ledger.ts")
def test_736_2_빠진_길_둘_신규_자재_기계_탭():
"""736-2 — 개인 층 재료 고름 머리 [신규 자재] · 엑셀 연결 = 줄 갈래대로 탭(노임 인력 · 기계 기계 · 자재 재료)."""
find = _text(M02 / "M02_MasterTemplete_Find_Pick.ts")
assert 'opt.layer === "personal" && !!opt.material && ok("material")' in find
assert "openNewMaterial(opt.owner" in find and "actions: [newMat]" in find
assert "machine?: (e: M01Element) => void;" in find
assert 'opt.machine && ok("machine")' in find
pick = _text(M02 / "M02_Material_Pick.ts")
assert "export function openNewMaterial(" in pick and "{ owner }," in pick
choose = _text(M02 / "M02_MasterTemplete_Hopyo_Choose.ts")
assert 'line.갈래 === "노임"' in choose and '["labor"]' in choose and '["machine"]' in choose
assert "element: (e) => opt.done([{ 노임: e.코드, 배: 1 }])," in choose
assert "machine: (e) => opt.done([{ 기계: e.코드, 배: 1 }])," in choose
left = _text(M02 / "M02_MasterTemplete_Hopyo_Left.ts")
assert "kinds: kindsOf(line)" in left and "pickMachine" not in left
lost = _text(M02 / "M02_MasterTemplete_Hopyo_Lost.ts")
assert '"machine"' in lost and "pickMachine" not in lost
unlinked = _text(M02 / "M02_MasterTemplete_Labor_Unlinked.ts")
assert 'machine: (e) => done("기계", e.코드)' in unlinked and "pickMachine" not in unlinked
@@ -115,9 +115,9 @@ def test_마법사_끝내기_막음_단가산출_칸_목록_열():
assert "unlinkedBar(doc, edit)" in _text("M02_MasterTemplete_Labor_Grid.ts")
assert "unlinkedBar(cur, edit &&" in _text("M02_MasterTemplete_Labor_Table.ts") # 일위대가표 띠
left = _text("M02_MasterTemplete_Hopyo_Left.ts")
assert (
"/logic-lib/elements?group=" in left and "M01_MasterData_UI_Logic_Api" not in left
) # 일반 사용자 길
# 일반 사용자 길 — 기계 고름은 찾아 넣기 창(736-2)이 `/logic-lib/elements` 로
assert "M01_MasterData_UI_Logic_Api" not in left
assert "/logic-lib/elements?group=" in _text("M02_MasterTemplete_Find_Pick.ts")
for name in (
"M02_MasterTemplete_Hopyo_Left.ts",
"M02_MasterTemplete_Hopyo_WizardSk.ts",
+1 -2
View File
@@ -226,6 +226,7 @@ export const ui_locales_m2 = {
M02_FindM01: ["M01 전체", "All M01"],
M02_FindClass: ["구분", "Class"],
M02_FindLogic: ["단가산출 기초", "Unit-cost logic"],
M02_FindNewMaterial: ["신규 자재", "New material"],
M02_FindElementLater: ["일위대가 표 개편 뒤", "After the unit-cost table rework"],
M02_FindOptions: ["옵션 {n}", "{n} options"],
// 729-3 불러오기 마법사 — 단가산출 단계 · 안 이은 줄 · 일위대가표 띠
@@ -297,7 +298,6 @@ export const ui_locales_m2 = {
M02_LeftChoose: ["고르기", "Pick"],
M02_LeftFind: ["찾기", "Find"],
M02_LeftMachine: ["기계", "Machine"],
M02_LeftMachineFind: ["기계 이름 · 규격으로 찾기", "Search machines by name or spec"],
M02_LeftNoneTip: [
"우리 M01 에 없음 — 엑셀 숫자 그대로 고정 줄(사용자가 고른 것으로 남음)",
"Not in M01 — keep the Excel numbers as a fixed row",
@@ -331,5 +331,4 @@ export const ui_locales_m2 = {
"Not in M01 — kept out of the SK with a note",
],
M02_LostClear: ["고름 지움", "Clear"],
M02_LostKind: ["{k} 줄 — 같은 갈래 요소만 이음", "{k} row — link the same kind only"],
} as const;