From 76ac1c499e76cb8b8a6dcd2e56d02ca17d028676 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 2 Nov 2026 10:14:07 +0900 Subject: [PATCH] =?UTF-8?q?feat(727-1):=20=EA=B3=A0=EB=A5=B4=EA=B8=B0=20?= =?UTF-8?q?=EC=B0=BD=20=EA=B8=B0=EC=B4=88=20=3D=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=EB=A7=88=EB=8B=A4=20=ED=95=9C=20=EC=A4=84=20=C2=B7=20[?= =?UTF-8?q?=EA=B8=B0=EB=B3=B8=20=EB=A1=9C=EC=A7=81=20=EC=84=A0=ED=83=9D]?= =?UTF-8?q?=20=EB=8B=A8=EC=B6=94=20=EA=B1=B7=EC=9D=8C=20=C2=B7=20=EC=B2=AB?= =?UTF-8?q?=20=EA=B8=B0=EC=B4=88=20=3D=20=EB=B0=94=ED=83=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기준 목록 옵션 줄(바꿈) 뺀 바탕 줄만 · 규격 칸 = 「옵션 N」(새 순수 함수 logic/M02_Find_Heads.ts) — 찾아 넣기 창 · 옛 창(Labor_Pick) 같이 · 옵션 줄 골라도 옵션 버리던 흠 걷음 · 서버 목록 그대로 - 단가산출 상세 [항목 추가] 기초 = 바탕 비었으면 바탕(옛 단추 동작 · 단위 비거나 식이면 기초 결과단위) · logicOnly 걷음 · locale M02_SangeunPickBase 지움 - 낡은 시험 test_721_find_pick 721-1 · 721-2 = 단추 걷음 · logicOnly 없음으로 고침 · 새 시험 test_727_sk_base_add - 기초 줄 1,461(옛 10,053) · GF000099 한 줄 「옵션 1」 Co-Authored-By: Claude Opus 5.5 --- .../M02_MasterTemplete_Find_Pick.ts | 80 +++++++++---------- .../M02_MasterTemplete_Labor_Pick.ts | 5 +- .../M02_MasterTemplete_Sangeun_Detail.ts | 40 ++++------ M02_MasterTemplete/logic/M02_Find_Heads.ts | 27 +++++++ resources/tester/test_721_find_pick.py | 13 +-- resources/tester/test_727_sk_base_add.py | 67 ++++++++++++++++ ui_template/ui_template_locale_m2.ts | 2 +- 7 files changed, 160 insertions(+), 74 deletions(-) create mode 100644 M02_MasterTemplete/logic/M02_Find_Heads.ts create mode 100644 resources/tester/test_727_sk_base_add.py diff --git a/M02_MasterTemplete/M02_MasterTemplete_Find_Pick.ts b/M02_MasterTemplete/M02_MasterTemplete_Find_Pick.ts index 191848fd2..ec5c7764a 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Find_Pick.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_Find_Pick.ts @@ -7,8 +7,9 @@ * · M02 단가산출 · 일위대가 = 위 층(마스터 › 회사 › 개인) 서고 목록(`/layers/{층}/templates`) · 고름 = 참조 {층 · 주인 · 키} * · 인력 · 재료 고름 = `element` · 일위대가 고정 줄 꼴 = `fixedOf`(705-1 이음 노임 LB · 자재 MT · 수량 1 은 부르는 쪽) · * `element` 없으면 그 줄 회색 막힘(까닭 = 일위대가 표 개편 뒤 · 723) - * · 부르는 곳 = M02 단가산출 [기본 로직 선택](`logicOnly`) · [항목 추가](`Sangeun_Pick`) · 일위대가 [로직 추가] + * · 부르는 곳 = M02 단가산출 [항목 추가](`Sangeun_Pick`) · 일위대가 [로직 추가] * (`Labor_Pick` 이 `lib.sangeun` 이면 이 창) — B09 · 불러오기는 옛 창(`Labor_Pick`) + * · (727-1) 기초 = 로직마다 한 줄(`logicHeads` · 옵션은 넣은 뒤 고름 · 옛 창도 같이) · 서버 목록 그대로 * ========================================================================== */ import { createDataTable } from "@ui/ui_template_data_table"; @@ -27,6 +28,7 @@ import { openStandardSheet } from "./M02_MasterTemplete_Labor_Sheet"; import { tc } from "./M02_MasterTemplete_Labor_Text"; import { ownOwner } from "./M02_MasterTemplete_Layer"; import { listLayerDocs, SANGEUN_KIND, type SangeunRow } from "./M02_MasterTemplete_Sangeun_Pick"; +import { logicHeads as heads } from "./logic/M02_Find_Heads"; import "../M01_MasterData/M01_MasterData_UI_Logic_Style.css"; // 창 겉틀 import "./M02_MasterTemplete_Labor.css"; @@ -53,10 +55,12 @@ export interface FindPickOptions { ref?: (ref: LaborRef, name: string) => void; /** M01 인력 · 재료 고름 — 없으면 그 줄 회색 막힘 */ element?: (e: M01Element) => void; - /** 로직 줄만(탭 · 인력 · 재료 없음 — [기본 로직 선택]) */ - logicOnly?: boolean; } +/** 727-1 기초마다 한 줄(옛 창 `Labor_Pick` 도 씀) — 규격 칸 = 「옵션 N」 */ +export const logicHeads = (rows: StandardRow[]): StandardRow[] => + heads(rows, (n) => L("M02_FindOptions").replace("{n}", String(n))); + /** 일위대가 고정 줄(705-1 이음 꼴) — 인력 = 노임 LB · 재료 = 자재 MT · 단가 = 지금 값(셈은 단가 책 값) */ export function fixedOf(e: M01Element): LaborFixed { const labor = e.종류 === "인력"; @@ -202,9 +206,7 @@ export function openFindPicker(opt: FindPickOptions): void { function draw(): void { const q = search.input.value.trim(); if (tab === "m01") { - shown = logicLines(q); - if (!opt.logicOnly) - shown.push(...elementLines("인력", people), ...elementLines("재료", goods)); + shown = [...logicLines(q), ...elementLines("인력", people), ...elementLines("재료", goods)]; } else { const words = q.toLowerCase().split(/\s+/).filter(Boolean); const kind = tab === "sk" ? L("M02_PickSangeun") : L("M02_PrecomboTitle"); @@ -220,7 +222,6 @@ export function openFindPicker(opt: FindPickOptions): void { /** 인력 · 재료 = 서버 찾기(친 낱말) — 재료는 낱말 있을 때만 · 늦게 온 답은 버림 */ async function findElements(): Promise { - if (opt.logicOnly) return; const mine = ++seq; const q = search.input.value.trim(); const get = (kind: "job" | "price"): Promise => @@ -244,21 +245,19 @@ export function openFindPicker(opt: FindPickOptions): void { timer = window.setTimeout(() => void findElements(), 250); }); - const tabs = opt.logicOnly - ? null - : createTabs({ - tabs: [ - { key: "m01", label: L("M02_FindM01") }, - { key: "sk", label: L("M02_PickSangeun") }, - { key: "pre", label: L("M02_PrecomboTitle") }, - ], - value: tab, - kind: "line", - onChange: (key) => { - tab = key as Tab; - draw(); - }, - }); + const tabs = createTabs({ + tabs: [ + { key: "m01", label: L("M02_FindM01") }, + { key: "sk", label: L("M02_PickSangeun") }, + { key: "pre", label: L("M02_PrecomboTitle") }, + ], + value: tab, + kind: "line", + onChange: (key) => { + tab = key as Tab; + draw(); + }, + }); ({ close } = openModal({ title: opt.title ?? L("M02_FindTitle"), @@ -271,7 +270,7 @@ export function openFindPicker(opt: FindPickOptions): void { el("div", { className: "m02-std-pick", attrs: { "data-find-pick": "" }, - children: [...(tabs ? [tabs.root] : []), search.root, hint, table.root], + children: [tabs.root, search.root, hint, table.root], }), ), })); @@ -283,29 +282,28 @@ export function openFindPicker(opt: FindPickOptions): void { void Promise.all([fetchLibLogics(), fetchStandard().catch(() => null)]) .then(([l, s]) => { logics = l; - std = s?.줄.length ? s.줄 : null; // 빈 줄 = 서버가 아직 셈 — 로직 줄 그대로 + std = s?.줄.length ? logicHeads(s.줄) : null; // 빈 줄 = 서버가 아직 셈 — 로직 줄 그대로 draw(); }) .catch(fail); void findElements(); - if (tabs) - void upper(opt.layer, opt.owner) - .then((scopes) => - Promise.all( - (["sk", "pre"] as const).map((t) => - listLayerDocs(scopes, t === "sk" ? SANGEUN_KIND : "precombo").then((rows) => { - lists[t] = rows; - tabs.setBadge(t, rows.length); - }), - ), + void upper(opt.layer, opt.owner) + .then((scopes) => + Promise.all( + (["sk", "pre"] as const).map((t) => + listLayerDocs(scopes, t === "sk" ? SANGEUN_KIND : "precombo").then((rows) => { + lists[t] = rows; + tabs.setBadge(t, rows.length); + }), ), - ) - .catch((error) => { - lists.sk ??= []; - lists.pre ??= []; - fail(error); - }) - .finally(draw); + ), + ) + .catch((error) => { + lists.sk ??= []; + lists.pre ??= []; + fail(error); + }) + .finally(draw); } /** 기준 목록이 오기 전 · 못 받으면 — 로직 하나 = 줄 하나(금액 빈칸) */ diff --git a/M02_MasterTemplete/M02_MasterTemplete_Labor_Pick.ts b/M02_MasterTemplete/M02_MasterTemplete_Labor_Pick.ts index 3e30c7c38..3e91c8a51 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Labor_Pick.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_Labor_Pick.ts @@ -9,6 +9,7 @@ * 704-1 단가산출 줄 = 기준 목록 표(품명 · 규격 · 단위 · 금액 · 출처 · 대표 규격 펼침 · 공종 거름 · * 실무 낱말 = 서버가 별칭에 덧붙임) · 금액 = 서버 M01 엔진(`/logic-lib/standard`) · 오기 전엔 로직 줄만 · * 줄 누름 = 계산서 보기(704-2 · `Labor_Sheet`) → [담기] = 지금 담기 동작(`done(로직)`) + * 727-1 기초 = 로직마다 한 줄(`logicHeads` — 옵션 줄을 고르면 옵션을 버리던 흠 걷음 · 옵션은 담은 뒤 고름) * 721-2 `lib.sangeun`(M02 일위대가 [로직 추가] · `withHopyo`) = 찾아 넣기 창(`Find_Pick` · 탭 M01 전체 · 단가산출 · * 일위대가 · 실무 호표 줄 없음) · M01 인력 · 재료 = `lib.fixed`(일위대가 고정 줄 · 없으면 회색 막힘) · * B09 · 불러오기는 이 창 그대로 @@ -38,7 +39,7 @@ import "../M01_MasterData/M01_MasterData_UI_Logic_Style.css"; // 창 겉틀 import "./M02_MasterTemplete_Labor.css"; import { openModal } from "@ui/ui_template_modal"; import { createText } from "@ui/ui_template_text"; -import { fixedOf, openFindPicker, upper } from "./M02_MasterTemplete_Find_Pick"; +import { fixedOf, logicHeads, openFindPicker, upper } from "./M02_MasterTemplete_Find_Pick"; export { withHopyo } from "./M02_MasterTemplete_Hopyo_Pick"; @@ -356,7 +357,7 @@ export function openLogicPicker( drawSubs(); find(); return fetchStandard().then((got) => { - std = got.줄.length ? got.줄 : null; // 빈 줄 = 서버가 아직 셈 — 로직 줄 그대로 + std = got.줄.length ? logicHeads(got.줄) : null; // 빈 줄 = 서버가 아직 셈 · 727-1 기초마다 한 줄 work.setOptions(opts(got.공종), ""); find(); }); diff --git a/M02_MasterTemplete/M02_MasterTemplete_Sangeun_Detail.ts b/M02_MasterTemplete/M02_MasterTemplete_Sangeun_Detail.ts index 1aa58d037..632731bc5 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Sangeun_Detail.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_Sangeun_Detail.ts @@ -6,7 +6,7 @@ * 시험 계산(`Logic_Calc` · 바탕 M01 로직 + 덮개 품 · 단가 사본) · 텍스트 수식(같은 사본 식) · 기본정보 = 바탕 로직(맨 위 접힘) * 고침 = SK 덮개 · 더한줄(`Sangeun_Pick`) · 뺀줄(꼴 = 서버 `M02_Sangeun_Doc`) · 값 = 서버 셈 길 최신 응답만 * 시험 입력 = SK 셈 `입력값`(저장 안 함) · [저장] = 화면 문서 → 서버가 다시 셈(원값 · 바탕판) - * 바탕 없는 SK = [기본 로직 선택] → 바탕 · 단위 · 바탕 없이 줄만도 됨 + * [항목 추가] 기초 = 바탕 비었으면 바탕(727-2 · 단위가 비거나 「식」이면 기초 결과단위) · 있으면 더한줄 · 바탕 없이 줄만도 됨 * ========================================================================== */ import { createBadge } from "@ui/ui_template_badge"; @@ -33,7 +33,6 @@ import { buildLabDetail } from "../M01_MasterData/M01_MasterData_UI_LogicLab_Det import { buildHo, type HoEdit } from "../M01_MasterData/M01_MasterData_UI_LogicLab_Ho"; import { createViewPicker } from "../M01_MasterData/M01_MasterData_UI_LogicLab_View"; import type { Layer } from "./M02_MasterTemplete_Api_Fetch"; -import { openFindPicker } from "./M02_MasterTemplete_Find_Pick"; import { calcSangeun, removedStrip } from "./M02_MasterTemplete_Sangeun_Cover"; import { openSangeunLinePicker } from "./M02_MasterTemplete_Sangeun_Pick"; import { @@ -100,6 +99,7 @@ export function mountSangeunDetail( size: 28, onClick: () => openSangeunLinePicker(scope, (line) => { + if ("종류" in line && line.종류 === "로직" && !doc.바탕) return setBase(line.로직); doc.더한줄 = [ ...(doc.더한줄 ?? []), line as unknown as NonNullable[number], @@ -108,25 +108,13 @@ export function mountSangeunDetail( }), }); addButton.hidden = !edit; - const pick = createButton({ - label: L("M02_SangeunPickBase"), - variant: "filled", - size: 28, - onClick: () => - openFindPicker({ - ...scope, - logicOnly: true, - title: L("M02_SangeunPickBase"), - logic: (x) => { - doc.바탕 = { 로직: x.키 }; - delete doc.바탕판; // 새 바탕 — 개정 견줄 판 없음 - if (!doc.단위 || doc.단위 === "식") doc.단위 = unitOf(x.결과단위) || doc.단위; - touch(false); - void loadBase(); - }, - }), - }); - pick.hidden = true; // 보임은 drawHead 가 정함 + /** 첫 기초 = 바탕(옛 [기본 로직 선택] 동작 · 단위는 `loadBase` 가 기초 결과단위로) */ + function setBase(key: string): void { + doc.바탕 = { 로직: key }; + delete doc.바탕판; // 새 바탕 — 개정 견줄 판 없음 + touch(false); + void loadBase(true); + } const baseHint = createText(L("M02_SangeunBaseViews")); host.replaceChildren( el("div", { @@ -135,7 +123,7 @@ export function mountSangeunDetail( children: [ head, notes, - el("div", { className: "m02-sk__head", children: [views.root, pick, addButton, baseHint] }), + el("div", { className: "m02-sk__head", children: [views.root, addButton, baseHint] }), editor, ], }), @@ -223,7 +211,6 @@ export function mountSangeunDetail( const baseName = base ? `${base.logic.키} ${base.logic.이름}` : null; const baseText = baseName ?? doc.바탕?.참조?.키 ?? L("M02_SangeunNoBase"); const revised = revisedBase(); - pick.hidden = !edit || !!doc.바탕 || !!doc.뺀줄?.length || !!Object.keys(doc.덮개 ?? {}).length; head.replaceChildren( field(L("M02_Name"), "이름"), field(L("M02_SangeunUnit"), "단위"), @@ -410,7 +397,8 @@ export function mountSangeunDetail( }); } - async function loadBase(): Promise { + /** `fresh` = 막 넣은 바탕 — 단위가 비거나 「식」이면 기초 결과단위(앞 수 뗌 · 배는 서버가 나눔) */ + async function loadBase(fresh = false): Promise { const key = baseKey(); base = null; text = null; @@ -422,6 +410,10 @@ export function mountSangeunDetail( } } if (!alive) return; + if (fresh && base && (!doc.단위 || doc.단위 === "식")) { + doc.단위 = unitOf(base.logic.결과단위) || doc.단위; + touch(false); + } draw(); await recalc(); // 바탕 정체(bare) 먼저 — 시험 계산 · 텍스트 수식 = 덮개 사본 if (alive) drawCalc(); diff --git a/M02_MasterTemplete/logic/M02_Find_Heads.ts b/M02_MasterTemplete/logic/M02_Find_Heads.ts new file mode 100644 index 000000000..0e75d1c1b --- /dev/null +++ b/M02_MasterTemplete/logic/M02_Find_Heads.ts @@ -0,0 +1,27 @@ +/* ============================================================================= + * M02_MasterTemplete/logic/M02_Find_Heads.ts + * 727-1 기준 목록(`/logic-lib/standard` · 로직 하나를 옵션 축마다 펼친 줄) → 기초마다 한 줄 — + * 바탕 줄(`바꿈` 빈 줄)만 · 규격 칸 = 옵션 축 수(`label(n)` · 없으면 빈칸) · 옵션은 넣은 뒤 고름. + * 쓰는 곳 = 찾아 넣기 창(`Find_Pick`) · 옛 창(`Labor_Pick`). 화면 · fetch 없음(`CLAUDE.md` 4장). + * ========================================================================== */ + +export interface HeadRow { + 키: string; + 규격: string; + 바꿈: Record; +} + +export function logicHeads(rows: T[], label: (n: number) => string): T[] { + const axes = new Map>(); + for (const r of rows) + for (const k of Object.keys(r.바꿈 ?? {})) { + if (!axes.has(r.키)) axes.set(r.키, new Set()); + axes.get(r.키)!.add(k); + } + return rows + .filter((r) => !Object.keys(r.바꿈 ?? {}).length) + .map((r) => { + const n = axes.get(r.키)?.size ?? 0; + return { ...r, 규격: n ? label(n) : "" }; + }); +} diff --git a/resources/tester/test_721_find_pick.py b/resources/tester/test_721_find_pick.py index 43aec8847..e22947946 100644 --- a/resources/tester/test_721_find_pick.py +++ b/resources/tester/test_721_find_pick.py @@ -1,7 +1,7 @@ """721 M02 찾아 넣기 창 — 단추 이름 · 자리 · 부르는 곳 · 인력 · 재료 줄 꼴(요구서_내역16 1장 5~7 · 2장 1 · 2 · 8). -- 721-1 단가산출 상세 = [기본 로직 선택]이 둘째 줄 [항목 추가] 왼쪽 -- 721-2 M02 세 곳(기본 로직 선택 · 항목 추가 · 일위대가 [로직 추가])만 새 창 · B09 · 불러오기는 옛 창 +- 721-1 단가산출 상세 = 둘째 줄 [항목 추가](727-2 [기본 로직 선택] 걷음 — 첫 기초가 바탕) +- 721-2 M02 두 곳(항목 추가 · 일위대가 [로직 추가])만 새 창 · B09 · 불러오기는 옛 창 - M01 전체 = 로직 · 인력(job) · 재료(price) · 기계 · 실무 호표 없음 - 고른 인력 · 재료 = 단가산출 더한줄 `{종류, 요소}` · 일위대가 고정 줄(705-1 이음 꼴) — 둘 다 서버가 받아 노임 · 자재 값으로 셈 """ @@ -26,10 +26,10 @@ def _text(path: Path) -> str: def test_721_1_단추_이름과_자리(): detail = _text(M02 / "M02_MasterTemplete_Sangeun_Detail.ts") - assert "children: [views.root, pick, addButton, baseHint]" in detail - assert " pick,\n );" not in detail # 머리 줄에서 뺌 + assert "children: [views.root, addButton, baseHint]" in detail + assert "M02_SangeunPickBase" not in detail # 727-2 단추 걷음 locale = _text(ROOT / "ui_template" / "ui_template_locale_m2.ts") - assert 'M02_SangeunPickBase: ["기본 로직 선택"' in locale + assert "M02_SangeunPickBase" not in locale assert 'M02_SangeunAddLine: ["항목 추가"' in locale @@ -39,7 +39,8 @@ def test_721_2_부르는_곳_셋만_새_창(): assert 'if (lib?.sangeun && lib.layer !== "project")' in labor_pick assert "return openFindPicker({" in labor_pick assert "findSangeun" not in labor_pick # 옛 창의 단가산출 탭 걷음 - assert "logicOnly: true," in _text(M02 / "M02_MasterTemplete_Sangeun_Detail.ts") + assert "logicOnly" not in _text(M02 / "M02_MasterTemplete_Sangeun_Detail.ts") # 727-2 + assert "logicOnly" not in find assert "openFindPicker({" in _text(M02 / "M02_MasterTemplete_Sangeun_Pick.ts") assert "sangeun: true," in _text(M02 / "M02_MasterTemplete_Hopyo_Pick.ts") # 일위대가 # 옛 창 그대로 — B09 · 불러오기는 sangeun 없이 부름 diff --git a/resources/tester/test_727_sk_base_add.py b/resources/tester/test_727_sk_base_add.py new file mode 100644 index 000000000..d5194a689 --- /dev/null +++ b/resources/tester/test_727_sk_base_add.py @@ -0,0 +1,67 @@ +"""727 M02 단가산출 기초 넣기(요구서_내역19 · 내역 20-1) — 임시 storage · 실제 M01 읽기만. + +- 727-1 고르기 창 기초 = 로직마다 한 줄(`logic/M02_Find_Heads.ts` · 옛 창도) · 규격 칸 = 「옵션 N」 +- 727-2 첫 기초 = 바탕 · 둘째부터 = 더한줄 세부 펼침(덮개 · 뺀줄 · M01 개정 지문 · 꼬리표) · B09 굳힘 같은 값 +- 727-3 넣은 뒤 고른 옵션 = SK `입력값` → 일위대가(펼침) · 굳힘도 그 옵션 +""" + +from __future__ import annotations + +import json +import re +import subprocess +from pathlib import Path + +from M01_MasterData import M01_MasterData_Store as m01 +from M02_MasterTemplete import M02_Logic_Standard as standard + +ROOT = Path(__file__).resolve().parents[2] +M02 = ROOT / "M02_MasterTemplete" +IMPORTS = re.compile(r"^import[^;]*;\n", re.MULTILINE) +GF = "GF000099" # 측구터파기 토사 · 원/㎥ · 입력 지역 · 보정작업 + + +def _node(files: list[str], body: str): + code = "".join(IMPORTS.sub("", (ROOT / f).read_text("utf-8")) for f in files) + got = subprocess.run( + ["node", "--experimental-strip-types", "--no-warnings", + "--input-type=module-typescript", "-"], + input=code + body, capture_output=True, text=True, encoding="utf-8", timeout=60, + ) # fmt: skip + assert got.returncode == 0, got.stderr + return json.loads(got.stdout) + + +def _heads(rows): + body = ( + f"\nconst rows = {json.dumps(rows, ensure_ascii=False, default=str)};" + "\nconsole.log(JSON.stringify(logicHeads(rows, (n) => `옵션 ${n}`)));" + ) + return _node(["M02_MasterTemplete/logic/M02_Find_Heads.ts"], body) + + +def test_727_1_기초마다_한_줄(): + whole = m01.loaded()[1] + rows = [] + for key in (GF, "GC000269", "GC001109"): + rows += standard._rows_of(whole, whole.logic(key), False) + assert sum(r["키"] == GF for r in rows) == 3 # 옵션 축 보정작업 두 값 + 바탕 + got = _heads(rows) + assert [r["키"] for r in got] == [GF, "GC000269", "GC001109"] + assert all(r["바꿈"] == {} for r in got) + one = got[0] + assert one["규격"] == "옵션 1" and one["금액"] == next(r for r in rows if r["키"] == GF)["금액"] + assert got[2]["규격"] == "" # 옵션 없는 기초 + listed = standard.listing() + if listed["준비"]: # 파생 저장본이 있을 때 — 1,461 안팎(로직 수) + heads = _heads([{k: r[k] for k in ("키", "규격", "바꿈")} for r in listed["줄"]]) + assert len(heads) == len({r["키"] for r in listed["줄"]}) + assert 1400 <= len(heads) <= 1600 + + +def test_727_1_두_창_모두_기초_한_줄(): + find = (M02 / "M02_MasterTemplete_Find_Pick.ts").read_text("utf-8") + old = (M02 / "M02_MasterTemplete_Labor_Pick.ts").read_text("utf-8") + assert "std = s?.줄.length ? logicHeads(s.줄) : null;" in find + assert "std = got.줄.length ? logicHeads(got.줄) : null;" in old + assert "logicOnly" not in find diff --git a/ui_template/ui_template_locale_m2.ts b/ui_template/ui_template_locale_m2.ts index 965184cde..8e0f29d28 100644 --- a/ui_template/ui_template_locale_m2.ts +++ b/ui_template/ui_template_locale_m2.ts @@ -203,7 +203,6 @@ export const ui_locales_m2 = { M02_SangeunRemoved: ["뺀 줄", "Removed rows"], M02_SangeunUnit: ["단위(1 단위 값)", "Unit (value per 1)"], M02_SangeunNeed: ["비우면 저장 안 됨", "Required"], - M02_SangeunPickBase: ["기본 로직 선택", "Select base logic"], M02_SangeunAddLine: ["항목 추가", "Add item"], M02_SangeunDrop: ["빼기", "Remove"], M02_SangeunRestore: ["되살리기", "Restore"], @@ -251,4 +250,5 @@ export const ui_locales_m2 = { M02_FindClass: ["구분", "Class"], M02_FindLogic: ["단가산출 로직", "Unit-cost logic"], M02_FindElementLater: ["일위대가 표 개편 뒤", "After the unit-cost table rework"], + M02_FindOptions: ["옵션 {n}", "{n} options"], } as const;