feat(842-2): 기계 비활성 — M01 기계 목록 「사용」 스위치 · 끈 기계는 찾기 · 고르기 · 잇기에서 뺌 · 해상 기계 79 대 끔
- 표지 = 기계 줄 맨 위 칸 `비활성: true`(없음 = 켜짐 · 값 묶음 밖) · 틀 검사 = 기계 줄에만 true(check_master OFF) - M01 기계 목록 = 끝 열 「사용」 스위치(초안 → 페이지 [저장] · 자동저장 없음) · 끈 줄 흐림 · 머리 「비활성 같이 보기」(끄면 서버 rows state 「켜짐」) - 뺌 = 요소 찾기 창(elements · 찾아 넣기 · 단가산출 기계 창) · logic-lib rows · M01 시험 계산 기계 고르기 표 · 내 기계 「비슷한 기계에서 시작」 · 불러오기 기계 잇기(M02_Machine_Find 표) · 기계경비식 「기계」 옵션(새 GET /api/m02/df/machine-off · 고른 값은 남기고 「· 비활성 기계」 꼬리) - 셈 길은 그대로 — 끈 기계를 쓰던 단가산출 · 사본 값 같음 - 자료 = 해상장비 79 대(펌프 준설선 13 · 그래브 준설선 10 · 하천골재채취선 7 · 기중기선 5 · 예선 9 · 양묘선 11 · 토운선 6 · 이우선 2 · 대선 16) 끔 - 시험 = test_842_2_machine_off 새로 · test_804_10 옵션 글 단언을 새 글로 고침(끈 기계 거름이 들어간 줄) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -17,7 +17,7 @@ import { createInputField, createSelectField } from "@ui/ui_template_input";
|
||||
import { currentLanguageIndex } from "@ui/ui_template_locale";
|
||||
import { createText } from "@ui/ui_template_text";
|
||||
import { formatLineMoney, formatMoney } from "../common_util/common_util_money";
|
||||
import { nameMachines, optionText } from "./formula_doc_options";
|
||||
import { liveOptions, nameMachines, optionText } from "./formula_doc_options";
|
||||
import "./calc_sheet.css";
|
||||
|
||||
export interface SheetLine {
|
||||
@@ -164,7 +164,7 @@ export function varRow(spec: SheetInput, value: string, onInput: (v: string) =>
|
||||
let control: HTMLElement;
|
||||
if (spec.고르기?.length) {
|
||||
const list = [...(value ? [] : [""]), ...spec.고르기.map(String)];
|
||||
const items = () => list.map((o) => ({ value: o, text: optionText(o) })); // 기계 번호 = 이름(요구 692-2)
|
||||
const items = () => liveOptions(list, value).map((o) => ({ value: o, text: optionText(o) })); // 기계 번호 = 이름(요구 692-2) · 끈 기계 뺌(842-2)
|
||||
const f = createSelectField({
|
||||
options: items(),
|
||||
value,
|
||||
|
||||
@@ -9,21 +9,31 @@
|
||||
import { createButton } from "@ui/ui_template_button";
|
||||
import { createInputField, createSelectField } from "@ui/ui_template_input";
|
||||
import { varId, type DocVar, type FormulaDoc } from "../common_util/common_util_formula_doc";
|
||||
import { cfMachineNames } from "../M02_MasterTemplete/DataFlow/M02_DF_Api";
|
||||
import { cfMachineNames, cfMachineOff } from "../M02_MasterTemplete/DataFlow/M02_DF_Api";
|
||||
import { mt } from "../M02_MasterTemplete/M02_MyMachine_Text";
|
||||
import { ft } from "./formula_doc_text";
|
||||
|
||||
const MACHINE = /^\d{4}-\d{4}$/;
|
||||
let names: Record<string, string> = {};
|
||||
let off = new Set<string>(); // 842-2 끈 기계 원문번호
|
||||
const machineInfo = (): Promise<Record<string, string>> =>
|
||||
Promise.all([cfMachineNames(), cfMachineOff()]).then(
|
||||
([got, gone]) => ((off = new Set(gone)), got),
|
||||
);
|
||||
|
||||
/** 고르기 글 — 기계 원문번호는 이름 · 규격을 앞에 */
|
||||
/** 고르기 글 — 기계 원문번호는 이름 · 규격을 앞에 · 끈 기계는 「· 비활성 기계」(842-2) */
|
||||
export const optionText = (x: string, known: Record<string, string> = names): string =>
|
||||
known[x] ? `${known[x]} (${x})` : x;
|
||||
known[x] ? `${known[x]} (${x})${off.has(x) ? ` · ${ft("Machine_Off")}` : ""}` : x;
|
||||
|
||||
/** 고르기 목록 — 끈 기계 뺌(지금 고른 값은 남김 · 842-2) */
|
||||
export const liveOptions = (list: string[], now: string): string[] =>
|
||||
list.filter((x) => x === now || !off.has(x));
|
||||
|
||||
/** 목록이 모두 원문번호(빈 칸 빼고)면 이름을 받아 `redraw`(글만 다시) — 수식 탭 옵션 · 단가산출 상세 옵션 */
|
||||
export function nameMachines(list: string[], redraw: () => void): void {
|
||||
const codes = list.filter(Boolean);
|
||||
if (codes.length && codes.every((x) => MACHINE.test(x)) && codes.some((x) => !(x in names)))
|
||||
void cfMachineNames().then((got) => {
|
||||
void machineInfo().then((got) => {
|
||||
names = got;
|
||||
redraw();
|
||||
});
|
||||
@@ -35,7 +45,7 @@ export function machineText(x: unknown, redraw: () => void): string | null {
|
||||
if (typeof x !== "string" || !MACHINE.test(x)) return null;
|
||||
if (!(x in names) && !asked) {
|
||||
asked = true;
|
||||
void cfMachineNames().then((got) => {
|
||||
void machineInfo().then((got) => {
|
||||
names = { ...names, ...got };
|
||||
redraw();
|
||||
});
|
||||
@@ -59,7 +69,8 @@ export function buildOptions(
|
||||
const now = k in given ? given[k] : v.연결?.값;
|
||||
if (kind === "선택") {
|
||||
const list = (v.연결?.목록 ?? []).map(String);
|
||||
const items = () => list.map((x) => ({ value: x, text: optionText(x) }));
|
||||
const items = () =>
|
||||
liveOptions(list, String(now ?? "")).map((x) => ({ value: x, text: optionText(x) }));
|
||||
const f = createSelectField({
|
||||
label,
|
||||
options: items(),
|
||||
|
||||
@@ -49,6 +49,7 @@ const TEXT = {
|
||||
By_Line: ["식 줄로 정의", "Defined by a line"],
|
||||
Pick_First: ["찾아 고름", "Search and pick"],
|
||||
Material: ["재료 고르기", "Material pick"],
|
||||
Machine_Off: ["비활성 기계", "Inactive machine"],
|
||||
None: ["없음", "None"],
|
||||
Open_Sub: ["식 펼침", "Show equation"],
|
||||
Bad_Sign: ["기호 꼴 아님 · 같은 기호 있음", "Bad or duplicate symbol"],
|
||||
|
||||
@@ -236,7 +236,7 @@ def find(q: str, limit: int = 30) -> list[dict]:
|
||||
for r in _rows():
|
||||
hay = " ".join(str(r.get(k) or "") for k in ("이름", "규격", "별칭", "상세구분", "키"))
|
||||
hay = hay.lower()
|
||||
if all(w in hay for w in words):
|
||||
if all(w in hay for w in words) and not r.get("비활성"): # 끈 기계 뺌(842-2)
|
||||
out.append(_brief(r))
|
||||
if len(out) >= limit:
|
||||
break
|
||||
|
||||
@@ -241,8 +241,10 @@ def rows(
|
||||
hits = [r for r in hits if sub in {r.get(s) for s in cm.MERGED.values()}]
|
||||
if detail: # 인력 상세구분 · 기계 상세구분(기종)
|
||||
hits = [r for r in hits if r.get("상세구분") == detail]
|
||||
if state: # 인력 상태
|
||||
hits = [r for r in hits if r.get("상태") == state]
|
||||
if state: # 인력 상태 · 기계 「켜짐」 = 비활성 뺌(842-2)
|
||||
hits = [
|
||||
r for r in hits if (not r.get(cm.OFF) if state == "켜짐" else r.get("상태") == state)
|
||||
]
|
||||
if unlinked: # 품셈재료 — 아직 못 이은 줄
|
||||
hits = [r for r in hits if not r.get("연결")]
|
||||
page, size = max(page, 1), min(max(size, 1), 500)
|
||||
@@ -463,7 +465,7 @@ def elements(group: str, q: str, limit: int) -> dict:
|
||||
if data.get("그룹") != group:
|
||||
continue
|
||||
for row in data.get(items_key(data)) or []:
|
||||
if _hit(row, q):
|
||||
if _hit(row, q) and not row.get(cm.OFF): # 끈 기계 뺌(842-2)
|
||||
more = {"별칭": row.get("별칭") or ""} if group == "로직" else {} # 208-1
|
||||
hits.append({**_brief(str(row.get("키")), row), "file": name, **more})
|
||||
return {"total": len(hits), "items": hits[: min(max(limit, 1), 200)]}
|
||||
|
||||
@@ -70,7 +70,8 @@ export function openPickTable(opt: PickTableOptions): void {
|
||||
const mine = ++seq;
|
||||
try {
|
||||
const q = search.value.trim();
|
||||
const got = await fetchRows(FILES[opt.kind], page, size, q);
|
||||
const on = opt.kind === "기계" ? "켜짐" : ""; // 끈 기계 뺌(842-2)
|
||||
const got = await fetchRows(FILES[opt.kind], page, size, q, false, "", "", on);
|
||||
if (mine !== seq) return; // 최신 응답만
|
||||
if (!got.total && fallback) {
|
||||
const next = [...(q.includes(" ") ? [q.split(/\s+/)[0]] : []), ...(opt.alt ?? [])].find(
|
||||
|
||||
@@ -66,3 +66,17 @@ table.m01-master__grid.m01-machine__grid {
|
||||
max-height: 240px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* 「사용」 열(842-2) — 스위치 · 끈 줄은 흐림 */
|
||||
.m01-machine__grid th.m01-machine__use {
|
||||
width: 64px;
|
||||
}
|
||||
|
||||
.m01-machine__grid td.m01-machine__use {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.m01-machine__row.is-off td:not(.m01-machine__use) {
|
||||
color: var(--color-text-muted);
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
@@ -3,14 +3,17 @@
|
||||
* M01 기계 목록(PLAN 821-2 · 요구 790-2) — 표는 목록용 열만(이름 · 규격 · 구분 · 가격 · 손료계수 ·
|
||||
* 주연료 · 조종원 · 시간당 경비(서버 값) · 별칭 · 비고) · 줄을 누르면 상세 페이지 · [새 기계] = 양식.
|
||||
* 단위 · 출처 열 없음 · 고친 것 · 새 줄 · 지울 줄은 초안(노랑 · 새 · 지움 표시) — 저장은 페이지 [저장].
|
||||
* 「사용」 열(842-2) = 스위치 · 끄면 줄 맨 위 칸 `비활성: true`(초안 → [저장]) · 끈 줄은 흐림 ·
|
||||
* 머리 「비활성 같이 보기」 끄면 서버가 끈 줄을 뺌(`rows` state 「켜짐」).
|
||||
* ========================================================================== */
|
||||
|
||||
import { createButton, el, showToast } from "@ui/ui_template_elements";
|
||||
import { t as L } from "@ui/ui_template_locale";
|
||||
import { createEmpty } from "@ui/ui_template_empty";
|
||||
import { createToggle } from "@ui/ui_template_toggle";
|
||||
import { guessRows, pageKeepFirst, watchFit } from "@ui/ui_template_pager";
|
||||
import { fetchRows, type Row, type RowsPage } from "./M01_MasterData_Api_Fetch";
|
||||
import { onDraftChange, peek, same } from "./M01_MasterData_Draft";
|
||||
import { editRow, onDraftChange, peek, same } from "./M01_MasterData_Draft";
|
||||
import { fetchMachineHourly } from "./M01_MasterData_Machine_Api";
|
||||
import { buildPager } from "./M01_MasterData_UI_Cells";
|
||||
import { saveView, type Pick } from "./M01_MasterData_UI_Side";
|
||||
@@ -38,6 +41,8 @@ const COLS: [string, (r: Row) => string][] = [
|
||||
],
|
||||
];
|
||||
const val = (r: Row): Record<string, unknown> => (r["값"] ?? {}) as Record<string, unknown>;
|
||||
const OFF = "비활성"; // 842-2 기계 줄 맨 위 칸 — true 만 · 켜짐 = 칸 없음
|
||||
let showOff = true; // 「비활성 같이 보기」(페이지 안에서 기억)
|
||||
|
||||
/** 돌려받은 함수 = 걷기. `open(target)` = 상세(키 · 새 줄 `add:<차례>` · 새 기계 `new`). */
|
||||
export function renderMachines(
|
||||
@@ -53,9 +58,31 @@ export function renderMachines(
|
||||
const hourly = new Map<string, string>();
|
||||
|
||||
const hourText = (key: string): string => hourly.get(key) ?? "…";
|
||||
/** 「사용」 칸 — 스위치(새 줄은 빈 칸) · 바꾸면 초안 */
|
||||
const useCell = (row: Row, cur: Row, key: string): HTMLElement => {
|
||||
const td = el("td", { className: "m01-machine__use" });
|
||||
if (!key || !data) return td;
|
||||
const version = data.version;
|
||||
const sw = createToggle({
|
||||
label: "",
|
||||
size: 20,
|
||||
checked: !cur[OFF],
|
||||
onChange: (on) => {
|
||||
const next: Row = { ...cur };
|
||||
if (on) delete next[OFF];
|
||||
else next[OFF] = true;
|
||||
editRow(file, version, key, row, next);
|
||||
},
|
||||
});
|
||||
sw.root.setAttribute("aria-label", L("M01_MachineUse"));
|
||||
sw.root.addEventListener("click", (e) => e.stopPropagation());
|
||||
td.classList.toggle("is-changed", !!cur[OFF] !== !!row[OFF]);
|
||||
td.append(sw.root);
|
||||
return td;
|
||||
};
|
||||
const line = (row: Row, cur: Row, cls: string, target: string, key = ""): HTMLElement => {
|
||||
const tr = el("tr", {
|
||||
className: `m01-machine__row ${cls}`.trim(),
|
||||
className: `m01-machine__row ${cls} ${cur[OFF] ? "is-off" : ""}`.trim(),
|
||||
attrs: { "data-key": key || target },
|
||||
children: [
|
||||
...COLS.map(([, get]) => {
|
||||
@@ -71,6 +98,7 @@ export function renderMachines(
|
||||
}),
|
||||
el("td", { text: String(cur["별칭"] ?? ""), attrs: { title: String(cur["별칭"] ?? "") } }),
|
||||
el("td", { text: String(cur["비고"] ?? ""), attrs: { title: String(cur["비고"] ?? "") } }),
|
||||
useCell(row, cur, key),
|
||||
],
|
||||
});
|
||||
tr.addEventListener("click", () => open(target));
|
||||
@@ -91,12 +119,21 @@ export function renderMachines(
|
||||
}
|
||||
const add = createButton({ label: L("M01_MachineNew"), variant: "ghost", iconStart: "plus" });
|
||||
add.addEventListener("click", () => open("new"));
|
||||
const offSwitch = createToggle({
|
||||
label: L("M01_MachineShowOff"),
|
||||
checked: showOff,
|
||||
onChange: (on) => {
|
||||
showOff = on;
|
||||
page = 1;
|
||||
void load();
|
||||
},
|
||||
});
|
||||
const empty =
|
||||
d.rows.length || draft?.adds.length
|
||||
? []
|
||||
: [createEmpty({ text: L("M01_NoRows"), small: true })];
|
||||
host.replaceChildren(
|
||||
el("div", { className: "ui-toolbar m01-master__bar", children: [add] }),
|
||||
el("div", { className: "ui-toolbar m01-master__bar", children: [add, offSwitch.root] }),
|
||||
el("div", {
|
||||
className: "ui-table-wrap m01-master__grid-wrap m01-machine__wrap",
|
||||
children: [
|
||||
@@ -111,6 +148,7 @@ export function renderMachines(
|
||||
el("th", { text: `${L("M01_MachineHourly")}(원)` }),
|
||||
el("th", { text: "별칭" }),
|
||||
el("th", { text: "비고" }),
|
||||
el("th", { className: "m01-machine__use", text: L("M01_MachineUse") }),
|
||||
],
|
||||
}),
|
||||
],
|
||||
@@ -152,7 +190,8 @@ export function renderMachines(
|
||||
const load = async (): Promise<void> => {
|
||||
const mine = ++ticket;
|
||||
try {
|
||||
const got = await fetchRows(file, page, size, q, false, pick.sub, pick.detail, pick.state);
|
||||
const state = showOff ? pick.state : "켜짐";
|
||||
const got = await fetchRows(file, page, size, q, false, pick.sub, pick.detail, state);
|
||||
if (mine !== ticket) return;
|
||||
data = got;
|
||||
saveView(pick.group, { page: data.page });
|
||||
|
||||
@@ -28,3 +28,9 @@ export function cfMachineNames(): Promise<Record<string, string>> {
|
||||
.catch(() => ((machineNames = null), {}));
|
||||
return machineNames;
|
||||
}
|
||||
|
||||
/** 끈 기계 원문번호(842-2) — 이름과 함께 한 번 받아 둠(못 받으면 빈 목록) */
|
||||
export const cfMachineOff = (): Promise<string[]> =>
|
||||
fetch(`${API_BASE_URL}/m02/df/machine-off`, { credentials: "include" })
|
||||
.then((r) => (r.ok ? (r.json() as Promise<string[]>) : []))
|
||||
.catch(() => []);
|
||||
|
||||
@@ -33,6 +33,17 @@ def get_machine_names() -> dict:
|
||||
return out
|
||||
|
||||
|
||||
@router.get("/machine-off")
|
||||
def get_machine_off() -> list[str]:
|
||||
"""끈 기계 원문번호(842-2) — 그 번호 기계가 모두 「비활성」 · 옵션 고르기 목록에서 뺌(고른 값은 남김)."""
|
||||
on, off = set(), set()
|
||||
for data in store.loaded()[0].values():
|
||||
if data.get("그룹") == "기계":
|
||||
for row in data.get(store.items_key(data)) or []:
|
||||
(off if row.get("비활성") else on).add(str(row.get("원문번호") or ""))
|
||||
return sorted(off - on - {""})
|
||||
|
||||
|
||||
@router.get("/standards")
|
||||
def get_standards() -> dict:
|
||||
try:
|
||||
|
||||
@@ -48,7 +48,8 @@ def get_materials(
|
||||
def get_rows(file: str, page: int = 1, size: int = 50, q: str = "", sub: str = "") -> dict:
|
||||
if not file.startswith(ROW_FILES):
|
||||
raise HTTPException(status_code=403, detail="요소 파일(인력 · 재료 · 기계)만 읽음")
|
||||
return m01.get_rows(file, page, size, q, False, sub, "", "")
|
||||
on = "켜짐" if file.startswith("기계") else "" # 끈 기계 뺌(842-2)
|
||||
return m01.get_rows(file, page, size, q, False, sub, "", on)
|
||||
|
||||
|
||||
@router.get("/table")
|
||||
|
||||
@@ -145,7 +145,7 @@ class Row:
|
||||
|
||||
@lru_cache(maxsize=4)
|
||||
def _table(whole: Any) -> tuple[list[Row], frozenset[str], dict[str, Row]]:
|
||||
rows = [Row(r) for r in whole.numbers.get("EQ", {}).values()]
|
||||
rows = [Row(r) for r in whole.numbers.get("EQ", {}).values() if not r.get("비활성")] # 842-2
|
||||
words = frozenset(w for r in rows for w in r.words)
|
||||
return rows, words, {r.key: r for r in rows}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ def find(q: str, limit: int = 30) -> list[dict]:
|
||||
if all(w in f"{r.get('이름')} {r.get('규격')} {r.get('키')}".lower() for w in words)
|
||||
]
|
||||
exact = (m01.loaded()[1].numbers.get("EQ") or {}).get(q.strip())
|
||||
head = [exact] if exact is not None and not mym.mine(exact.get("키")) else []
|
||||
head = [exact] if exact and not mym.mine(exact.get("키")) and not exact.get("비활성") else []
|
||||
m01_rows = [*head, *_m01m().find(q, limit)]
|
||||
seen = set()
|
||||
for r in m01_rows:
|
||||
|
||||
@@ -136,6 +136,9 @@ COLS = {
|
||||
}
|
||||
COLS["한국은행환율"] = COLS["유가전력"]
|
||||
ALIAS = "별칭" # 요소 줄 덧칸(815-2) — 열 한 벌 밖 · 있으면 빈 글 아님
|
||||
OFF = (
|
||||
"비활성" # 기계 줄 맨 위 칸(842-2) — 있으면 true 만 · 없음 = 켜짐 · 찾기 · 고르기 · 잇기에서 뺌
|
||||
)
|
||||
|
||||
|
||||
def _cols_of(row: dict) -> tuple:
|
||||
@@ -299,6 +302,8 @@ def check_form(name: str, data: dict) -> list[str]:
|
||||
out += _check_labor(where, row)
|
||||
if ALIAS in row and not (isinstance(row[ALIAS], str) and row[ALIAS].strip()):
|
||||
out.append(f"{where} · 「{ALIAS}」 이 빈 글 · 글 아님(빈 칸이면 칸을 두지 않음)")
|
||||
if OFF in row and (row[OFF] is not True or data.get("그룹") != "기계"):
|
||||
out.append(f"{where} · 「{OFF}」 는 기계 줄에만 true(켜짐이면 칸을 두지 않음)")
|
||||
if cols and _cols_of(row) != cols:
|
||||
out.append(f"{where} · 열이 한 벌이 아님 — 「{' · '.join(cols)}」")
|
||||
if book == "자재품목" and row.get(mm.LOAD) not in mm.LOADS:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000541",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0003",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000542",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0006",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000543",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0010",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000544",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0012",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000545",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0020",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000546",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0022",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000547",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0033",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000548",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0040",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000549",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0044",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000550",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0060",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000551",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0080",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000552",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0120",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000553",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0200",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000554",
|
||||
"비활성": true,
|
||||
"원문번호": "9020-0010",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000555",
|
||||
"비활성": true,
|
||||
"원문번호": "9020-0015",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000556",
|
||||
"비활성": true,
|
||||
"원문번호": "9020-0016",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000557",
|
||||
"비활성": true,
|
||||
"원문번호": "9020-0022",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000558",
|
||||
"비활성": true,
|
||||
"원문번호": "9020-0035",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000559",
|
||||
"비활성": true,
|
||||
"원문번호": "9020-0050",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000560",
|
||||
"비활성": true,
|
||||
"원문번호": "9020-0072",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000561",
|
||||
"비활성": true,
|
||||
"원문번호": "9020-0160",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000562",
|
||||
"비활성": true,
|
||||
"원문번호": "9020-0180",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000563",
|
||||
"비활성": true,
|
||||
"원문번호": "9020-0200",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000564",
|
||||
"비활성": true,
|
||||
"원문번호": "9030-0016",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000565",
|
||||
"비활성": true,
|
||||
"원문번호": "9030-0018",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000566",
|
||||
"비활성": true,
|
||||
"원문번호": "9030-0025",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000567",
|
||||
"비활성": true,
|
||||
"원문번호": "9030-0035",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000568",
|
||||
"비활성": true,
|
||||
"원문번호": "9030-0045",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000569",
|
||||
"비활성": true,
|
||||
"원문번호": "9030-0050",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000570",
|
||||
"비활성": true,
|
||||
"원문번호": "9030-0080",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000571",
|
||||
"비활성": true,
|
||||
"원문번호": "9030-0100",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000572",
|
||||
"비활성": true,
|
||||
"원문번호": "9030-0240",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000573",
|
||||
"비활성": true,
|
||||
"원문번호": "9040-0010",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000574",
|
||||
"비활성": true,
|
||||
"원문번호": "9040-0030",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000575",
|
||||
"비활성": true,
|
||||
"원문번호": "9040-0050",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000576",
|
||||
"비활성": true,
|
||||
"원문번호": "9040-0060",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000577",
|
||||
"비활성": true,
|
||||
"원문번호": "9040-0100",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000578",
|
||||
"비활성": true,
|
||||
"원문번호": "9040-0120",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000579",
|
||||
"비활성": true,
|
||||
"원문번호": "9040-0200",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000580",
|
||||
"비활성": true,
|
||||
"원문번호": "9040-0250",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000581",
|
||||
"비활성": true,
|
||||
"원문번호": "9040-0300",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000582",
|
||||
"비활성": true,
|
||||
"원문번호": "9040-0380",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000583",
|
||||
"비활성": true,
|
||||
"원문번호": "9040-0680",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000584",
|
||||
"비활성": true,
|
||||
"원문번호": "9050-0075",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000585",
|
||||
"비활성": true,
|
||||
"원문번호": "9050-0150",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000586",
|
||||
"비활성": true,
|
||||
"원문번호": "9050-0450",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000587",
|
||||
"비활성": true,
|
||||
"원문번호": "9050-0750",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000588",
|
||||
"비활성": true,
|
||||
"원문번호": "9050-0850",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000589",
|
||||
"비활성": true,
|
||||
"원문번호": "9060-0060",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000590",
|
||||
"비활성": true,
|
||||
"원문번호": "9060-0100",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000591",
|
||||
"비활성": true,
|
||||
"원문번호": "9060-0200",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000592",
|
||||
"비활성": true,
|
||||
"원문번호": "9060-0300",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000593",
|
||||
"비활성": true,
|
||||
"원문번호": "9060-0500",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000594",
|
||||
"비활성": true,
|
||||
"원문번호": "9060-0600",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000595",
|
||||
"비활성": true,
|
||||
"원문번호": "9070-0015",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000596",
|
||||
"비활성": true,
|
||||
"원문번호": "9070-0020",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000597",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-0050",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000598",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-0080",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000599",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-0100",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000600",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-0120",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000601",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-0150",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000602",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-0200",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000603",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-0300",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000604",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-0500",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000605",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-0700",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000606",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-1000",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000607",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-1100",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000608",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-1400",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000609",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-1500",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000610",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-1750",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000611",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-2000",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000612",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-3000",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000613",
|
||||
"비활성": true,
|
||||
"원문번호": "9090-0800",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000614",
|
||||
"비활성": true,
|
||||
"원문번호": "9090-1000",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000615",
|
||||
"비활성": true,
|
||||
"원문번호": "9090-1200",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000616",
|
||||
"비활성": true,
|
||||
"원문번호": "9090-1300",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000617",
|
||||
"비활성": true,
|
||||
"원문번호": "9090-1400",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000618",
|
||||
"비활성": true,
|
||||
"원문번호": "9090-1500",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000619",
|
||||
"비활성": true,
|
||||
"원문번호": "9090-1600",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -35,8 +35,11 @@ def test_기계_이름_표_문서_목록_전수() -> None:
|
||||
|
||||
def test_옵션_부품_글만_바꿈() -> None:
|
||||
src = (ROOT / "A00_Common/formula_doc_options.ts").read_text(encoding="utf-8")
|
||||
assert "known[x] ? `${known[x]} (${x})` : x" in src
|
||||
assert "list.map((x) => ({ value: x, text: optionText(x) }))" in src
|
||||
assert "known[x] ? `${known[x]} (${x})${off.has(x)" in src # 842-2 끈 기계 꼬리
|
||||
assert (
|
||||
'liveOptions(list, String(now ?? "")).map((x) => ({ value: x, text: optionText(x) }))'
|
||||
in src
|
||||
)
|
||||
assert "nameMachines(list, () => f.setOptions(items(), f.select.value));" in src
|
||||
api = (ROOT / "M02_MasterTemplete/DataFlow/M02_DF_Api.ts").read_text(encoding="utf-8")
|
||||
assert "machineNames ??= fetch(" in api # 한 번만 받음
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
"""842-2 기계 비활성 — 지킴 시험(쓰기는 임시 사본만).
|
||||
|
||||
① 틀 검사 — 기계 줄 맨 위 칸 `비활성` = true 만 · 다른 그룹 줄 · false 는 오류
|
||||
② 저장 왕복 — M01 저장 길이 표지를 받고(그 대 파일 하나) 지우면 칸이 사라짐 · 셈 값 그대로
|
||||
③ 끈 기계는 찾기 · 고르기 · 잇기에서 안 나옴 — 요소 찾기 창 · 비슷한 기계 · 불러오기 기계 잇기 ·
|
||||
목록 거름 「켜짐」 · 끈 원문번호 목록 · 셈(시간당)은 그대로
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from M01_MasterData import M01_MasterData_Store as store
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT / "resources" / "master_data" / "scripts"))
|
||||
import master_machine_files as mmf # noqa: E402
|
||||
|
||||
from M01_MasterData import M01_MasterData_Machine as m01m # noqa: E402
|
||||
from M02_MasterTemplete import M02_Machine_Find as mfind # noqa: E402
|
||||
from M02_MasterTemplete import M02_Machine_Hourly as mh # noqa: E402
|
||||
from M02_MasterTemplete.DataFlow import M02_DF_Router as dfr # noqa: E402
|
||||
|
||||
REAL = store.FOLDER
|
||||
cm, mk, M = store.cm, store.mk, mmf.NAME
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def master(tmp_path: Path, monkeypatch) -> Path:
|
||||
for path in REAL.glob("*.json"):
|
||||
if not path.name.startswith("_") or path.name == mk.BOOK.name:
|
||||
shutil.copy(path, tmp_path / path.name)
|
||||
shutil.copytree(REAL / mmf.DIR, tmp_path / mmf.DIR)
|
||||
monkeypatch.setattr(store, "FOLDER", tmp_path)
|
||||
return tmp_path
|
||||
|
||||
|
||||
def _rows() -> list[dict]:
|
||||
return store.read(M)[0]["줄"]
|
||||
|
||||
|
||||
def _part(*changes: dict) -> dict:
|
||||
return {"file": M, "version": store.read(M)[1], "changes": list(changes)}
|
||||
|
||||
|
||||
# ① 틀 검사 ──────────────────────────────────────────────────────────
|
||||
def test_form_check() -> None:
|
||||
data = copy.deepcopy(store.read(M)[0])
|
||||
data["줄"] = data["줄"][:3]
|
||||
assert cm.check_form(M, data) == []
|
||||
data["줄"][0][cm.OFF] = True
|
||||
assert cm.check_form(M, data) == []
|
||||
data["줄"][0][cm.OFF] = False
|
||||
assert any(cm.OFF in e for e in cm.check_form(M, data))
|
||||
labor = copy.deepcopy(store.read("인력.json")[0])
|
||||
labor["줄"] = labor["줄"][:1]
|
||||
labor["줄"][0][cm.OFF] = True
|
||||
assert any(cm.OFF in e for e in cm.check_form("인력.json", labor))
|
||||
|
||||
|
||||
# ② 저장 왕복 ───────────────────────────────────────────────────────
|
||||
def test_save_round_trip(master: Path) -> None:
|
||||
key = next(r["키"] for r in _rows() if not r.get(cm.OFF) and r["이름"].startswith("불도저"))
|
||||
row = copy.deepcopy(next(r for r in _rows() if r["키"] == key))
|
||||
before = mh.hourly(key)
|
||||
snap = {p.name: p.read_bytes() for p in (master / mmf.DIR).glob("*.json")}
|
||||
store.save([_part({"op": "edit", "key": key, "row": {**row, cm.OFF: True}})])
|
||||
after = {p.name: p.read_bytes() for p in (master / mmf.DIR).glob("*.json")}
|
||||
assert {n for n in after if after[n] != snap[n]} == {f"{key}.json"}
|
||||
got = next(r for r in _rows() if r["키"] == key)
|
||||
assert got[cm.OFF] is True and got["값"] == row["값"]
|
||||
assert mh.hourly(key) == before # 셈은 그대로(쓰던 단가산출 · 사본이 깨지지 않음)
|
||||
assert key not in {x["ref"] for x in store.elements("기계", "불도저", 200)["items"]}
|
||||
store.save([_part({"op": "edit", "key": key, "row": row})])
|
||||
assert (master / mmf.DIR / f"{key}.json").read_bytes() == snap[f"{key}.json"]
|
||||
|
||||
|
||||
# ③ 찾기 · 고르기 · 잇기에서 뺌 ──────────────────────────────────────
|
||||
def _off() -> list[dict]:
|
||||
got = [r for r in _rows() if r.get(cm.OFF)]
|
||||
if not got:
|
||||
pytest.skip("끈 기계 없음")
|
||||
return got
|
||||
|
||||
|
||||
def test_hidden_everywhere() -> None:
|
||||
off = _off()
|
||||
keys = {r["키"] for r in off}
|
||||
for r in off[:: max(1, len(off) // 6)]:
|
||||
name = str(r["상세구분"] or r["이름"]).replace(" ", "")
|
||||
assert not keys & {x["ref"] for x in store.elements("기계", name, 200)["items"]}
|
||||
assert not keys & {x["키"] for x in m01m.find(name, 200)}
|
||||
with mfind.m01_only():
|
||||
got = mfind.find(r["이름"], r.get("규격") or "")
|
||||
assert got["키"] not in keys and not keys & {x["키"] for x in got["후보"]}
|
||||
listed = store.rows(M, 1, 500, "", state="켜짐")
|
||||
assert not keys & {r["키"] for r in listed["rows"]}
|
||||
assert listed["total"] == len(_rows()) - len(keys)
|
||||
assert store.rows(M, 1, 1, "")["total"] == len(_rows()) # 같이 보기 = 전부
|
||||
codes = {str(r["원문번호"]) for r in off} - {
|
||||
str(r["원문번호"]) for r in _rows() if not r.get(cm.OFF)
|
||||
}
|
||||
assert set(dfr.get_machine_off()) == codes
|
||||
assert all(c in dfr.get_machine_names() for c in codes) # 고른 값 글은 그대로
|
||||
|
||||
|
||||
def test_sea_machines_off() -> None:
|
||||
"""842-2 ㉣ 해상 기계([90]해상장비 = 선원류 칸 있는 대)는 끔 — 사용자가 다시 켤 수 있으므로 켠 대는 알림만."""
|
||||
sea = [r for r in _rows() if "해상" in str(r.get("구분"))]
|
||||
assert len(sea) == 79
|
||||
on = [r["키"] for r in sea if not r.get(cm.OFF)]
|
||||
if on:
|
||||
pytest.skip(f"다시 켠 해상 기계 {len(on)} 대 — 사용자 판단")
|
||||
@@ -49,6 +49,8 @@ export const ui_locales_machine = {
|
||||
M01_MachineNew: ["새 기계", "New machine"],
|
||||
M01_MachineBack: ["목록으로", "Back to list"],
|
||||
M01_MachineHourly: ["시간당 경비", "Hourly cost"],
|
||||
M01_MachineUse: ["사용", "Active"],
|
||||
M01_MachineShowOff: ["비활성 같이 보기", "Show inactive"],
|
||||
M01_MachineMeta: ["원문 · 구분", "Source · group"],
|
||||
M01_MachineUses: ["쓰는 곳", "Used by"],
|
||||
M01_MachineNoUses: ["쓰는 곳 없음", "Not used"],
|
||||
|
||||
Reference in New Issue
Block a user