fix(716-3): Hopyo 화면 글 「사전조합」 → 「일위대가」 — M02_PrecomboTitle 키로

- 확인 창 [일위대가 생성] 단추 · 엑셀 보기 · 다시 잇기 · 장부 · 고르기 알림 · 구조물 조합으로 설명
- 서버 줄 상태 글 「확인된 일위대가」
- test_m02_hopyo_screen 단추 글 단언을 키 꼴로 고침

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-01 15:42:14 +09:00
co-authored by Claude Opus 5.5
parent 7128579f92
commit cfc5309a4d
9 changed files with 32 additions and 15 deletions
+1 -1
View File
@@ -251,7 +251,7 @@ def match_line(line: dict[str, Any], ctx: Ctx, full: bool = True) -> dict[str, A
target = {"호표": no, "이름": h.get("이름", ""), "단위": h.get("단위"), "입력값": {}}
target.update(못읽음=[], 출처={}, 환산=conv, 몫=None, 배=conv["배"])
ok = bool(made) and conv["배"] is not None and conv["갈래"] != "추정"
text = f"확인된 사전조합 {made}" if made else f"제{no}호표 먼저 확인"
text = f"확인된 일위대가 {made}" if made else f"제{no}호표 먼저 확인"
return {**out, "상태": "호표", "체크": ok, "대상": [target], "글": text}
if kind == "노임":
return {**out, "상태": "노임", "글": "노임 · 그릇 없음"}
@@ -26,6 +26,7 @@ import {
} from "@ui/ui_template_elements";
import { loadingError, showLoading } from "@ui/ui_template_loading";
import { openModal } from "@ui/ui_template_modal";
import { t as L } from "@ui/ui_template_locale";
import { createSourceView } from "../M01_MasterData/M01_MasterData_UI_LogicLab_Source";
import {
buildHopyo,
@@ -429,7 +430,7 @@ export function buildHopyoCheck(opt: CheckOptions): HTMLElement {
const ask = whole
? `호표 통째로 만듦 — ${h.통째!.대상.map(targetText).join(" · ")}`
: `품셈 줄 ${put.length}${diff ? `(품 다름 ${diff})` : ""} · 고정 줄 ${h.줄.length - put.length - outs}` +
` · 뺌 ${outs} — ${money}(미리 셈 · 끝수 차 있음) — 사전조합 생성`;
` · 뺌 ${outs} — ${money}(미리 셈 · 끝수 차 있음) — ${L("M02_PrecomboTitle")} 생성`;
if (!(await showConfirmDialog(ask, "생성"))) return;
const from = { 층: opt.scope.layer, 주인: opt.scope.owner, 키: opt.key, 번: opt.no };
showLoadingOverlay();
@@ -530,7 +531,11 @@ export function buildHopyoCheck(opt: CheckOptions): HTMLElement {
draw();
});
const whole = !!h.통째 && (d.통째 ?? h.통째.체크);
const go = createButton({ label: "사전조합 생성", variant: "filled", size: "sm" });
const go = createButton({
label: `${L("M02_PrecomboTitle")} 생성`,
variant: "filled",
size: "sm",
});
go.setAttribute("data-hopyo-make", "");
go.addEventListener("click", () => void make(h));
tools.push(all);
@@ -573,7 +578,7 @@ export function buildHopyoCheck(opt: CheckOptions): HTMLElement {
...(h.만든
? [
note(
`만든 사전조합 ${h.만든.키}${h.만든.확인 ? " · 확인함" : ""}` +
`만든 ${L("M02_PrecomboTitle")} ${h.만든.키}${h.만든.확인 ? " · 확인함" : ""}` +
(h.만든.품셈
? ` · 품셈 줄 ${h.만든.품셈.length} · 고정 줄 ${h.만든.고정 ?? 0}`
: ""),
@@ -12,6 +12,7 @@
* ========================================================================== */
import { createButton, el } from "@ui/ui_template_elements";
import { t as L } from "@ui/ui_template_locale";
import type { HopyoLine, HopyoPart } from "./M02_MasterTemplete_Hopyo_Api";
import {
checkable,
@@ -170,7 +171,10 @@ function actsCell(line: HopyoLine, parts: HopyoPart[], ctx: RowsContext): HTMLEl
createButton({ label, variant: "ghost", size: "sm", onClick: run });
const out = el("select", {
className: "ui-input ui-input--sm",
attrs: { title: "로직없음 · 사용안함 — 사전조합에 안 넣고 장부에 남김", "data-out": "" },
attrs: {
title: `로직없음 · 사용안함 — ${L("M02_PrecomboTitle")}에 안 넣고 장부에 남김`,
"data-out": "",
},
children: OUT_TEXT.map(([value, text]) => el("option", { text, attrs: { value } })),
}) as HTMLSelectElement;
out.value = ctx.d.뺌[line.차례] ?? "";
@@ -11,6 +11,7 @@
import { createButton, el, showToast } from "@ui/ui_template_elements";
import { loadingError, showLoading } from "@ui/ui_template_loading";
import { t as L } from "@ui/ui_template_locale";
import {
readLedger,
saveLedger,
@@ -326,7 +327,7 @@ export function buildHopyoLedger(opt: LedgerOptions): HTMLElement {
className: "ui-toolbar m02-hopyo__bar",
children: [
el("strong", { text: LEDGER_TITLE }),
note(`${LEDGER_HINT} · 사전조합 생성 때 남음 · 아래 층이 이김`),
note(`${LEDGER_HINT} · ${L("M02_PrecomboTitle")} 생성 때 남음 · 아래 층이 이김`),
search,
kind,
...(opt.canWrite ? [save, drop] : []),
@@ -7,6 +7,7 @@
* ========================================================================== */
import { showToast } from "@ui/ui_template_elements";
import { t as L } from "@ui/ui_template_locale";
import { listHopyoPicks, type HopyoPickRow } from "./M02_MasterTemplete_Hopyo_Api";
import type { LibraryPick, PickExtra } from "./M02_MasterTemplete_Labor_Pick";
import { tc } from "./M02_MasterTemplete_Labor_Text";
@@ -82,7 +83,7 @@ export function hopyoExtra(row: HopyoPickRow, lib: LibraryPick): PickExtra {
export async function pickHopyo(row: HopyoPickRow, lib: LibraryPick): Promise<void> {
const alias = row.이름.slice(0, ALIAS_MAX).trim() || null;
if (row.확인) {
showToast(`「${row.이름}」 → 확인한 사전조합 ${row.확인.키}`, "success");
showToast(`「${row.이름}」 → 확인한 ${L("M02_PrecomboTitle")} ${row.확인.키}`, "success");
return lib.pick(row.확인, row.이름, alias);
}
try {
@@ -12,6 +12,7 @@ import { createButton, showToast } from "@ui/ui_template_elements";
import { openModal } from "@ui/ui_template_modal";
import { createDataTable, type DataRow } from "@ui/ui_template_data_table";
import { createText } from "@ui/ui_template_text";
import { t as L } from "@ui/ui_template_locale";
import {
pageScope,
relinkApply,
@@ -43,7 +44,7 @@ const won = (v: number | null | undefined): string =>
export function relinkKeys(keys: string[]): RelinkSource {
const scope = pageScope();
return {
title: `이 공사 단가로 다시 잇기 — 사전조합 ${keys.length}`,
title: `이 공사 단가로 다시 잇기 — ${L("M02_PrecomboTitle")} ${keys.length}`,
priceLabel: "최신 판 단가",
rows: () => relinkRows(scope, { 키: keys }),
apply: async (changes) => {
@@ -51,7 +52,7 @@ export function relinkKeys(keys: string[]): RelinkSource {
const sum = (k: "전" | "후"): number => got.문서.reduce((a, d) => a + (d[k] ?? 0), 0);
return {
back: got.되돌림,
note: `바뀐 줄 ${got.바뀜} · 사전조합 ${got.문서.length} · 1 단위 값 합 ${won(sum("전"))} → ${won(sum("후"))}`,
note: `바뀐 줄 ${got.바뀜} · ${L("M02_PrecomboTitle")} ${got.문서.length} · 1 단위 값 합 ${won(sum("전"))} → ${won(sum("후"))}`,
};
},
};
@@ -24,6 +24,7 @@ import {
showToast,
} from "@ui/ui_template_elements";
import { openModal } from "@ui/ui_template_modal";
import { t as L } from "@ui/ui_template_locale";
import type { Sheet } from "../A00_Common/spreadsheet/spreadsheet_types";
import type { SpreadsheetHandle } from "../A00_Common/spreadsheet/spreadsheet";
import { StaleError } from "./M02_MasterTemplete_Api_Fetch";
@@ -239,7 +240,7 @@ export function openHopyoSheet(opt: HopyoSheetOptions): void {
variant: "danger",
size: "sm",
onClick: async () => {
const ask = `실무자료 「${opt.파일}」을 지움 — 만든 사전조합 · 별칭 장부는 그대로`;
const ask = `실무자료 「${opt.파일}」을 지움 — 만든 ${L("M02_PrecomboTitle")} · 별칭 장부는 그대로`;
if (!(await showConfirmDialog(ask, "삭제"))) return;
try {
await deleteHopyo(opt.scope, opt.key);
@@ -427,7 +428,7 @@ export function openHopyoSheet(opt: HopyoSheetOptions): void {
const nos = [...checked].sort((a, b) => a - b);
const fresh = nos.filter((no) => !hasDraft(draftKey(opt.scope, opt.key, no))).length;
const ask =
`고른 호표 ${nos.length}개를 체크한 줄로 사전조합 생성` +
`고른 호표 ${nos.length}개를 체크한 줄로 ${L("M02_PrecomboTitle")} 생성` +
(fresh ? ` — 확인 창에서 안 고친 ${fresh}개는 서버 제안 체크 줄` : "");
if (!(await showConfirmDialog(ask, "저장"))) return;
go.disabled = true;
@@ -510,7 +511,10 @@ export function openHopyoSheet(opt: HopyoSheetOptions): void {
}
if (ok) document.dispatchEvent(new Event(HOPYO_BUILT));
result.replaceChildren(
table(["호표", "사전조합", "빠진 줄", "값 차이(참고)"], [...rows, ...skipped]),
table(
["호표", `${L("M02_PrecomboTitle")}`, "빠진 줄", "값 차이(참고)"],
[...rows, ...skipped],
),
);
const all = order.length + skipped.length;
showToast(`만듦 ${ok} / ${all}`, ok === all ? "success" : "warning");
@@ -526,7 +530,7 @@ export function openHopyoSheet(opt: HopyoSheetOptions): void {
/** [모두 고정으로 들여오기](705-3) — 물음 한 번 · 서버가 호표 모두를 하위부터(숫자 = 서버가 파일에서) */
async function importAll(): Promise<void> {
const ask =
`호표 ${briefs.length}개를 엑셀 숫자 그대로(고정 줄) 사전조합으로 들여옴 — ` +
`호표 ${briefs.length}개를 엑셀 숫자 그대로(고정 줄) ${L("M02_PrecomboTitle")}으로 들여옴 — ` +
"이미 만든 호표는 그대로 · 품셈 맞춤은 뒤에 호표 [수정]에서 [품셈으로 바꾸기]";
if (!(await showConfirmDialog(ask, "들여오기"))) return;
fixAll.disabled = true;
@@ -17,6 +17,7 @@ import { createCheckbox } from "@ui/ui_template_checkbox";
import { createInputField } from "@ui/ui_template_input";
import { openModal } from "@ui/ui_template_modal";
import { createText } from "@ui/ui_template_text";
import { t as L } from "@ui/ui_template_locale";
import {
buildHopyoItem,
hopyoSiblings,
@@ -101,7 +102,7 @@ export async function toItem(o: ToItemOptions): Promise<void> {
actions.append(go);
body.append(
createText(
"체크한 줄 = 품셈 줄 · 나머지 = 고정 줄(엑셀 숫자 그대로) — 사전조합을 거치지 않고 구조물 조합 항목을 바로 만듦",
`체크한 줄 = 품셈 줄 · 나머지 = 고정 줄(엑셀 숫자 그대로) — ${L("M02_PrecomboTitle")}을 거치지 않고 구조물 조합 항목을 바로 만듦`,
"muted",
),
name.root,
+1 -1
View File
@@ -308,7 +308,7 @@ def test_옛_즉시_확정과_바로_만들기_없음():
users = sorted(name for name, text in texts.items() if "buildHopyo(" in text)
assert users == ["Hopyo_Check", "Hopyo_Sheet"] # 확인 창 · 엑셀 보기 [저장] 물음 뒤
check = texts["Hopyo_Check"]
assert '"사전조합 생성"' in check and "맞음 · 별칭 일괄 체크" in check
assert '`${L("M02_PrecomboTitle")} 생성`' in check and "맞음 · 별칭 일괄 체크" in check
assert "확인하고" not in check # 359-4 명사형
assert "showLoadingOverlay()" in check and "dropDraft(k)" in check
assert "m02-hopyo-check:" in check and "POLL_MS" in check