From 00514598f0166d185ad49e62927404b24dbcb55e Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sun, 15 Nov 2026 13:45:18 +0900 Subject: [PATCH] =?UTF-8?q?feat(836-2):=20M02=20=EB=8B=A8=EA=B0=80?= =?UTF-8?q?=EC=82=B0=EC=B6=9C=20=ED=99=94=EB=A9=B4=20=E2=80=94=20=EC=97=B4?= =?UTF-8?q?=20=EB=95=8C=20=E3=80=8CM01=20=EC=9D=B4=20=EB=B0=94=EB=80=8C?= =?UTF-8?q?=EC=96=B4=20=EB=8B=A4=EC=8B=9C=20=EB=B3=B5=EC=82=AC=ED=95=A8?= =?UTF-8?q?=E3=80=8D=20=EC=95=8C=EB=A6=BC(=ED=95=AD=EB=AA=A9=20=C2=B7=20M0?= =?UTF-8?q?1=20=EC=A0=84=20=E2=86=92=20=ED=9B=84=20=C2=B7=20=EC=A7=80?= =?UTF-8?q?=EC=9B=8C=EC=A7=84=20=EB=82=B4=20=EA=B3=A0=EC=B9=A8)=20=C2=B7?= =?UTF-8?q?=20=EC=9E=90=EB=8F=99=20=EC=B8=B5=20[=EB=8B=A8=EA=B0=80=20?= =?UTF-8?q?=EA=B0=B1=EC=8B=A0]=20=C2=B7=20[=EC=9E=AC=EC=83=9D=EC=84=B1]=20?= =?UTF-8?q?=C2=B7=20=E3=80=8C=EC=83=88=20=EA=B0=92=E3=80=8D=20=C2=B7=20?= =?UTF-8?q?=E3=80=8C=EC=82=AC=EB=B3=B8=20=EC=97=86=EB=8A=94=20=EC=A4=84?= =?UTF-8?q?=E3=80=8D=20=EA=B1=B7=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 새 formula_doc_renew.ts(확인창 부품 + 표) · [확인] = 저장(다시 안 뜸) · [나중에] = 다음에 또 - line_stack_screen 두 곳(단추 줄 조건 · 열기 뒤 알림) · 프로젝트 층 = 단추 그대로 - 내 기계 글 셋 = 「새 값」 → [단가 갱신] 안내를 자동 따라옴으로 · 알림 글 금액 줄 꼴 다듬음 Co-Authored-By: Claude Opus 5.5 --- A00_Common/formula_doc_copy.ts | 10 ++++-- A00_Common/formula_doc_renew.ts | 45 ++++++++++++++++++++++++ A00_Common/formula_doc_text.ts | 10 ++++++ A00_Common/line_stack_screen.ts | 6 +++- M02_MasterTemplete/M02_MyMachine_Text.ts | 6 ++-- M02_MasterTemplete/M02_Sk_AutoRenew.py | 2 +- 6 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 A00_Common/formula_doc_renew.ts diff --git a/A00_Common/formula_doc_copy.ts b/A00_Common/formula_doc_copy.ts index 1b74e2e7c..1e0f92075 100644 --- a/A00_Common/formula_doc_copy.ts +++ b/A00_Common/formula_doc_copy.ts @@ -8,6 +8,7 @@ * (821-3) 값 줄만 바뀜(M01 단가 · 내 기계 고침) = 「새 값 있음」(같은 자리 · 같은 꼴) · 지운 키(M01 · 내 기계) = 「지워짐 n」 · * [단가 갱신] 확인 창 = 바뀐 자원 줄(이름 · 바뀐 칸 전 → 후 · 지워짐 = 마지막 값 유지) * (822-1) 값 사본에 없는 셈 줄(옛 저장본 빈틈) = 「사본 없는 줄 n」(지금 값으로 셈 · 저장하면 굳음) + * (836-2) 표지 `자동`(프로젝트 층 밖 = M01 자동 따라옴) = 단추 · 「새 값」 · 「사본 없는 줄」 안 그림(지워짐만) * ========================================================================== */ import { createBadge } from "@ui/ui_template_badge"; @@ -18,6 +19,7 @@ import type { FormulaDoc } from "../common_util/common_util_formula_doc"; import type { Basis } from "../common_util/common_util_formula_doc_import"; import { swappedCount } from "../common_util/common_util_line_stack_pick"; import { ft } from "./formula_doc_text"; +import type { RenewNote } from "./formula_doc_renew"; export type CopyJob = "단가갱신" | "재생성"; @@ -32,6 +34,10 @@ export interface CopyMark { 따로?: boolean; /** 가져온 식(엑셀 불러오기 · 807-1) 단가기준 — 엑셀 = 엑셀 값 기억 · 우리 = 연결 값 */ 단가기준?: Basis; + /** M01 자동 따라옴(836-2 · 프로젝트 층 밖) — [단가 갱신] · [재생성] · 「새 값」 · 「사본 없는 줄」 없음 */ + 자동?: boolean; + /** 열 때 알림(836-2) — M01 에서 바뀐 항목의 지운 고침 */ + 갱신?: RenewNote[]; } export interface CopyOptions { @@ -58,7 +64,7 @@ export function isStale(m: CopyMark | undefined): boolean { export function copyBadges(m: CopyMark | undefined, edit: boolean): HTMLElement[] { const out: HTMLElement[] = []; const doc = !!m?.M01새판 || !!m?.문서새판?.length; - if (doc || m?.단가새값) { + if ((doc || m?.단가새값) && !m?.자동) { const b = createBadge(ft(doc ? "New_M01" : "New_Value"), "danger"); b.dataset.copyStale = doc ? "doc" : "value"; out.push(b); @@ -69,7 +75,7 @@ export function copyBadges(m: CopyMark | undefined, edit: boolean): HTMLElement[ b.dataset.copyGone = m.지운키.join(","); out.push(b); } - if (m?.사본없음) { + if (m?.사본없음 && !m.자동) { const b = createBadge(`${ft("Gap")} ${m.사본없음}`, "warning"); b.title = ft("Gap_Note"); b.dataset.copyGap = String(m.사본없음); diff --git a/A00_Common/formula_doc_renew.ts b/A00_Common/formula_doc_renew.ts new file mode 100644 index 000000000..72b409f88 --- /dev/null +++ b/A00_Common/formula_doc_renew.ts @@ -0,0 +1,45 @@ +/* ============================================================================= + * formula_doc_renew.ts + * M01 자동 따라옴 알림(PLAN 836-2 · 요구 854) — M02 단가산출(프로젝트 층 밖)을 열 때 + * M01 에서 바뀐 항목에 얹힌 내 고침을 지우고 다시 복사한 것을 한 번 보임(서버 `M02_Sk_AutoRenew.notes`) + * 표 = 항목 · M01 전 → 후 · 지워진 내 고침 · [확인] = 저장(다시 안 뜸) · [나중에] = 다음에 열면 또 뜸 + * 고침 없는 항목은 조용히 따라옴(알림 없음) + * ========================================================================== */ + +import { askConfirm, el } from "@ui/ui_template_elements"; +import { createTable } from "@ui/ui_template_table"; +import { ft } from "./formula_doc_text"; + +/** 서버 알림 한 줄 — 갈래 = 문서(바탕 수식) · 부름(굳힌 부른 문서) · 기계(기계 덮음) */ +export interface RenewNote { + 키: string; + 이름?: string; + 갈래?: string; + M01?: string[]; + 내고침?: string[]; +} + +/** 칸 안 여러 줄 — 줄마다 한 덩이 */ +const lines = (xs: string[] | undefined): HTMLElement => + el("div", { children: (xs ?? []).map((t) => el("div", { text: t })) }); + +/** 알림 창 — 확인이면 true(부르는 쪽이 저장) */ +export function askRenew(notes: RenewNote[]): Promise { + const table = createTable( + [ + { label: ft("Renew_Item"), width: "28%" }, + { label: ft("Renew_M01") }, + { label: ft("Renew_Mine") }, + ], + notes.map((n) => [lines([n.이름 || n.키, n.키]), lines(n.M01), lines(n.내고침)]), + { density: "data" }, + ); + table.dataset.renewNotes = String(notes.length); + return askConfirm({ + title: ft("Renew_Title"), + message: ft("Renew_Ask"), + okLabel: ft("Renew_Ok"), + cancelLabel: ft("Renew_Later"), + detail: table, + }); +} diff --git a/A00_Common/formula_doc_text.ts b/A00_Common/formula_doc_text.ts index dd7607219..a20e06c60 100644 --- a/A00_Common/formula_doc_text.ts +++ b/A00_Common/formula_doc_text.ts @@ -78,6 +78,16 @@ const TEXT = { Changed: ["바뀐 자원", "Changed resources"], Before_After: ["계", "Sum"], Job_Done: ["적용함", "Applied"], + Renew_Title: ["M01 이 바뀌어 다시 복사함", "Re-copied from updated M01"], + Renew_Ask: [ + "M01 에서 바뀐 항목에 있던 내 고침을 지우고 M01 에서 다시 복사함 — [확인] 하면 저장(다시 안 뜸)", + "Your edits on items changed in M01 were removed and re-copied — OK saves (no repeat)", + ], + Renew_Item: ["항목", "Item"], + Renew_M01: ["M01 전 → 후", "M01 before → after"], + Renew_Mine: ["지워진 내 고침", "Removed edits"], + Renew_Ok: ["확인", "OK"], + Renew_Later: ["나중에", "Later"], Basis_Excel: ["엑셀 단가 그대로", "Excel prices"], Basis_Ours: ["우리 단가로", "Our prices"], Dirty_First: [ diff --git a/A00_Common/line_stack_screen.ts b/A00_Common/line_stack_screen.ts index 3e917b35d..2ce9e5484 100644 --- a/A00_Common/line_stack_screen.ts +++ b/A00_Common/line_stack_screen.ts @@ -49,6 +49,7 @@ import { swapBadge, } from "./formula_doc_copy"; import { docFoot } from "./formula_doc_foot"; +import { askRenew } from "./formula_doc_renew"; import { buildOptions } from "./formula_doc_options"; import { ft } from "./formula_doc_text"; import { addTaken, type Taken } from "../common_util/common_util_line_stack_copy"; @@ -306,7 +307,7 @@ export function createStackScreen(opt: StackScreenOptions) { const busy = dirty(); const tools = [ ...(mark?.단가기준 && opt.page ? [basisPick(mark.단가기준)] : []), // 모달 = 머리 판 단추(793-3b) - ...(cp && edit() && key ? copyTools(true, (job) => void runJob(job)) : []), + ...(cp && edit() && key && !mark?.자동 ? copyTools(true, (job) => void runJob(job)) : []), // 836-2 자동 = 없음 ...(edit() && key && !opt.page ? [ createButton({ @@ -517,6 +518,9 @@ export function createStackScreen(opt: StackScreenOptions) { recalc(); draw(); opt.page?.(); // 캐시에서 이어 받은 고침 — 페이지 [저장] · [되돌림] 켬 + const notes = (mark as CopyMark | undefined)?.갱신; // 836-2 M01 자동 따라옴 알림(take 가 채움) + if (cp && edit() && !dirty() && notes?.length) + void askRenew(notes).then((ok) => ok && save({ 할일: "갱신확인", 판: version }, "")); } catch (e) { if (mine === seq) head.replaceChildren( diff --git a/M02_MasterTemplete/M02_MyMachine_Text.ts b/M02_MasterTemplete/M02_MyMachine_Text.ts index 28e68ff94..b4f313ab9 100644 --- a/M02_MasterTemplete/M02_MyMachine_Text.ts +++ b/M02_MasterTemplete/M02_MyMachine_Text.ts @@ -33,7 +33,7 @@ const TEXT = { Copied: "「{value}」 값을 복사 — 고친 뒤 [저장]", Own_Value: "개인 값", Own_Note: - "개인 값 = 이 기계 고유 값(가격 · 손료계수 · 연료 ℓ · 잡품 % · 조종원 수) · 단가(노임 · 유가)는 M01 값 · 단가산출엔 만들 때 복사 → 고친 뒤 「새 값」 → [단가 갱신]", + "개인 값 = 이 기계 고유 값(가격 · 손료계수 · 연료 ℓ · 잡품 % · 조종원 수) · 단가(노임 · 유가)는 M01 값 · 단가산출(프로젝트 밖)은 고치면 저절로 따라옴", Server_Hourly: "서버 셈 시간당", Hourly_None: "시간당 못 셈", Uses: "쓰는 단가산출", @@ -59,7 +59,7 @@ const TEXT = { Win_M01: "M01 기계", Win_Copy: "이 단가산출에 복사된 값", Win_Live: "원본 지금 값", - Win_Newer: "원본에 새 값 있음 — [단가 갱신] 으로 반영", + Win_Newer: "원본에 새 값 있음 — 프로젝트 사본은 [단가 갱신] 으로 반영", Win_Swap: "이 기계로 바꾸기", Win_Edit: "고쳐 쓰기", Win_Blank: "빈 양식에서 새 기계", @@ -67,7 +67,7 @@ const TEXT = { Win_Cancel: "취소", Win_Edit_Note: "[확인] = 이 단가산출에 초안으로 끼움 · 단가산출 [저장] 때 내 기계로 저장 · 저장 안 하고 나가면 사라짐", - Win_Uses: "이 기계를 쓰는 다른 단가산출 {value} 건 — 저장하면 그쪽에 「새 값」 배지", + Win_Uses: "이 기계를 쓰는 다른 단가산출 {value} 건 — 저장하면 그쪽도 새 값으로 셈", Win_Hint: "왼쪽에서 기계를 고르면 값이 보임", Win_View: "조회", Why_M01: "기계 값은 M01 기계 상세에서", diff --git a/M02_MasterTemplete/M02_Sk_AutoRenew.py b/M02_MasterTemplete/M02_Sk_AutoRenew.py index 0f9d8285f..db33ebaac 100644 --- a/M02_MasterTemplete/M02_Sk_AutoRenew.py +++ b/M02_MasterTemplete/M02_Sk_AutoRenew.py @@ -125,7 +125,7 @@ def diff(a: Any, b: Any) -> list[str]: if dc.fp(x) == dc.fp(y): continue name = str((y or x or {}).get("이름") or f"금액 {n + 1}") - show = lambda z: "—" if not z else f"{z.get('수량') or ''} × {z.get('단가') or ''}" # noqa: E731 + show = lambda z: " × ".join(str(z[k]) for k in ("수량", "단가") if z and z.get(k)) or "—" # noqa: E731 out.append(f"{name} {show(x)} → {show(y)}") for slot in ("식", "끝수", "결과단위"): if dc.fp(a.get(slot)) != dc.fp(b.get(slot)):