From 0ceadebd97672b56dfaec29422a1831bd63d512b Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sun, 15 Nov 2026 13:16:23 +0900 Subject: [PATCH 01/11] =?UTF-8?q?chore(ledger):=20=EC=9A=94=EA=B5=AC=20855?= =?UTF-8?q?=20=EC=9E=A5=EB=B6=80=20=E2=80=94=20=EC=9D=BC=EC=9C=84=EB=8C=80?= =?UTF-8?q?=EA=B0=80=20=EB=B6=88=EB=9F=AC=EC=98=A4=EA=B8=B0=20=EC=B8=B5=20?= =?UTF-8?q?=EA=B0=80=EB=A5=B4=EA=B8=B0=20=EA=B1=B7=EC=9D=8C(=EB=AC=B4?= =?UTF-8?q?=EC=A1=B0=EA=B1=B4=20=EA=B0=9C=EC=9D=B8=20=EC=B8=B5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude --- docs/raw/요구_1104/사용자요구_1104.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/raw/요구_1104/사용자요구_1104.md b/docs/raw/요구_1104/사용자요구_1104.md index b34544abc..1b467d2f4 100644 --- a/docs/raw/요구_1104/사용자요구_1104.md +++ b/docs/raw/요구_1104/사용자요구_1104.md @@ -1353,3 +1353,8 @@ est원본에서 잘린 글씨는 보이는 만큼만 서울/경기로 표현하 - 지금 내 기계와 내 재료의 운영이 조금 애매한것같은데 어떻게 생각하는지 말해줘. - 공용 단가산출의 페이지 구성에서 보기 방식을 단가산출 상세 보기 탭이 의미가 없어보임. 혼선만 주는것같음. 구성에서 제외 일단 위 내용으로 반영하고 나는 프로그래머가 아니라 알수 없으니 반영된 화면을 보고 판단하겠음. + +## 요구 855 (11-15 · 사용자 · 추가 업무 — 일위대가 불러오기의 개인 / 회사 / 마스터 분류 걷음 · 무조건 개인의 단가 · 일위대가로 가져옴 · 필요하면 본인이 공유) + +추가 업무 +- 일위대가 불러오기에 개인/회사/마스터가 분류되어 있음. 분류될 필요 없음. 무조건 개인의 단가나 일위대가로 가지고 오며 필요시 본인이 단가산출이나 일위대가로 공유하면 됨. From 71385ac6e20b69c6d466d1b76b2c169318df270a Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sun, 15 Nov 2026 13:17:10 +0900 Subject: [PATCH 02/11] =?UTF-8?q?chore(ledger):=20=EC=9A=94=EA=B5=AC=20856?= =?UTF-8?q?=20=EC=9E=A5=EB=B6=80=20=E2=80=94=20=EB=B6=88=EB=9F=AC=EC=98=A4?= =?UTF-8?q?=EA=B8=B0=20=EC=A0=80=EC=9E=A5=20=EC=A0=84=20=EB=8B=A8=EA=B0=80?= =?UTF-8?q?=EC=82=B0=EC=B6=9C=EB=8F=84=20=EC=88=98=EC=8B=9D=20=EB=B3=B4?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude --- docs/raw/요구_1104/사용자요구_1104.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/raw/요구_1104/사용자요구_1104.md b/docs/raw/요구_1104/사용자요구_1104.md index 1b467d2f4..2cf838237 100644 --- a/docs/raw/요구_1104/사용자요구_1104.md +++ b/docs/raw/요구_1104/사용자요구_1104.md @@ -1358,3 +1358,7 @@ est원본에서 잘린 글씨는 보이는 만큼만 서울/경기로 표현하 추가 업무 - 일위대가 불러오기에 개인/회사/마스터가 분류되어 있음. 분류될 필요 없음. 무조건 개인의 단가나 일위대가로 가지고 오며 필요시 본인이 단가산출이나 일위대가로 공유하면 됨. + +## 요구 856 (11-15 · 사용자 · 추가 업무 — 엑셀 불러오기에서 단가산출 수식 보기 탭이 「저장한 단가산출만」 이라고 나옴 · 단가산출 상세 보기가 없어질 예정이니 수식 보기가 되어야 함) + +추가 업무 엑셀불러오기에서 단가산출의 수식 보기 탭에 단가산출이 저장한 단가산출만 이라고 나옴. 단가산출 상세 보기가 없어질 예정이니 보기가 되어야함. From 9658783fab1dd019eb443b40776c1a3aa6a49f73 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sun, 15 Nov 2026 13:17:47 +0900 Subject: [PATCH 03/11] =?UTF-8?q?chore(ledger):=20=EC=9A=94=EA=B5=AC=20857?= =?UTF-8?q?=20=EC=9E=A5=EB=B6=80=20=E2=80=94=20=EB=8B=A8=EA=B0=80=EC=82=B0?= =?UTF-8?q?=EC=B6=9C=20=EB=B3=80=EC=88=98=20=EA=B8=B0=ED=98=B8(=ED=92=88?= =?UTF-8?q?=EC=85=88=20=EC=A0=95=EC=9D=98=20=EA=B8=B0=ED=98=B8=20=C2=B7=20?= =?UTF-8?q?=ED=95=98=EC=B2=A8=20=ED=98=95=ED=83=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude --- docs/raw/요구_1104/사용자요구_1104.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/raw/요구_1104/사용자요구_1104.md b/docs/raw/요구_1104/사용자요구_1104.md index 2cf838237..c5fb63bee 100644 --- a/docs/raw/요구_1104/사용자요구_1104.md +++ b/docs/raw/요구_1104/사용자요구_1104.md @@ -1362,3 +1362,8 @@ est원본에서 잘린 글씨는 보이는 만큼만 서울/경기로 표현하 ## 요구 856 (11-15 · 사용자 · 추가 업무 — 엑셀 불러오기에서 단가산출 수식 보기 탭이 「저장한 단가산출만」 이라고 나옴 · 단가산출 상세 보기가 없어질 예정이니 수식 보기가 되어야 함) 추가 업무 엑셀불러오기에서 단가산출의 수식 보기 탭에 단가산출이 저장한 단가산출만 이라고 나옴. 단가산출 상세 보기가 없어질 예정이니 보기가 되어야함. + +## 요구 857 (11-15 · 사용자 · 추가 업무 — 단가산출 변수 기호: 품셈에 이미 정의된 q · E · K · f · Cm 등은 단가에서 변경될 필요 없음 · 인건비 · 자재는 지금 하첨 형태 → 비슷한 형태로 변경 필요 · 뜻 확인 중) + +추가업무 +단가산출의 에서 변수들의 품셈에서 q, E, K, f, Cm등 벌써 정의 되어 있는데 단가에서 변경될 필요는 없으며 현재 인건비와 자재에서도 인력 하첨 형태로 나와있는데 비슷한 형태로 변경이 필요해 보임. From 6c5d070e2db3b6203db1a526a0b35ae2b10f7e22 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sun, 15 Nov 2026 13:28:09 +0900 Subject: [PATCH 04/11] =?UTF-8?q?chore(ledger):=20=EC=9A=94=EA=B5=AC=20858?= =?UTF-8?q?=20=EC=9E=A5=EB=B6=80=20=E2=80=94=20=EA=B8=B0=ED=98=B8=20?= =?UTF-8?q?=ED=91=9C=EA=B8=B0=20=EA=B2=BD=EC=9F=81=EC=82=AC=20=EB=8C=80?= =?UTF-8?q?=EC=A1=B0=20=C2=B7=20GC000089=20=EB=90=98=EB=8F=8C=EB=A6=BC=20?= =?UTF-8?q?=EC=95=88=20=EB=90=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude --- docs/raw/요구_1104/사용자요구_1104.md | 5 +++++ .../raw/요구_1104/요구858_기계경비_기호표기.png | Bin 0 -> 93525 bytes 2 files changed, 5 insertions(+) create mode 100644 docs/raw/요구_1104/요구858_기계경비_기호표기.png diff --git a/docs/raw/요구_1104/사용자요구_1104.md b/docs/raw/요구_1104/사용자요구_1104.md index c5fb63bee..c704c3604 100644 --- a/docs/raw/요구_1104/사용자요구_1104.md +++ b/docs/raw/요구_1104/사용자요구_1104.md @@ -1367,3 +1367,8 @@ est원본에서 잘린 글씨는 보이는 만큼만 서울/경기로 표현하 추가업무 단가산출의 에서 변수들의 품셈에서 q, E, K, f, Cm등 벌써 정의 되어 있는데 단가에서 변경될 필요는 없으며 현재 인건비와 자재에서도 인력 하첨 형태로 나와있는데 비슷한 형태로 변경이 필요해 보임. + +## 요구 858 (11-15 · 사용자 · 857 답 — 품셈에서 이미 정의된 기호는 좌우 영역에서 바꿀 필요 없음(사용자가 바꾸는 것은 별개) · 기계 시간당 경비의 P기계 · k기계상각비 표기 → 경쟁사 표현 확인(깔끔함) · 그림 요구858_기계경비_기호표기.png / GC000089 단가산출 기초 항목 하나 삭제 뒤 되돌림 안 됨 → 확인 · 개선 · 데이터 원복) + +기호 이름을 사용자가 바꾸면 어쩔수 없지만 벌써 품셈에서 정의된 기호라는거지. 좌우측 영역에서 바꿀 필요는 없다는 말이야. [Image #1] 여기를 보면 P기계, k 기계상각비 라고 표현되는데 경쟁사에서 어떻게 표현되어 있는지 확인해봐. 깔끔하잖아. +GC000089의 단가품셈 기초의 항목을 내가 하나 삭제했음. 되돌림 했는데 되돌림이 안됨. 확인 후 개선하고 데이터도 원복해줘. diff --git a/docs/raw/요구_1104/요구858_기계경비_기호표기.png b/docs/raw/요구_1104/요구858_기계경비_기호표기.png new file mode 100644 index 0000000000000000000000000000000000000000..0b8b1d2d14d63e7042657da4b8b719f1a56bdac4 GIT binary patch literal 93525 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 bX+Q Date: Sun, 15 Nov 2026 13:30:57 +0900 Subject: [PATCH 05/11] =?UTF-8?q?feat(836-1):=20=EA=B8=B0=EA=B3=84=20?= =?UTF-8?q?=EB=8D=AE=EC=9D=8C=20=EA=BC=B4=20=C2=B7=20=EA=B3=84=EC=82=B0?= =?UTF-8?q?=EA=B8=B0=20=EC=A7=9D=20=EA=B7=9C=EC=B9=99=20=C2=B7=20=EC=84=9C?= =?UTF-8?q?=EB=B2=84=20=EC=A0=80=EC=9E=A5=20=EC=A0=95=EB=A6=AC=20=E2=80=94?= =?UTF-8?q?=20SK=20=EC=88=98=EC=8B=9D=20=EB=AC=B8=EC=84=9C=20=EC=B9=B8=20?= =?UTF-8?q?=EA=B8=B0=EA=B3=84=EB=8D=AE=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 자리 = SK 수식.문서.기계덮음 {기계 입력 글: {값 · 조종원 줄 · 잡품 줄}} · 사본 줄 그대로 - 계산기(파이썬 · TS 짝)가 기계 식 부를 때 그 기계 덮음을 얹음 · 덮음 없음 = 문서 그대로 - 서버 저장 · 미리 셈 = 몸 검사 + 원본과 같은 칸 · 안 부르는 기계 뺌 - 거울 · 손셈(굴착기 1.0㎥ 108,958) · 저장 길 지킴 시험 test_836_1 Co-Authored-By: Claude Opus 5.5 --- M02_MasterTemplete/M02_Doc_Sk_Path.py | 13 + common_util/common_util_formula_doc_calc.ts | 16 +- common_util/common_util_formula_doc_cover.ts | 298 +++++++++++++++ .../master_data/scripts/master_doc_calc.py | 20 +- .../master_data/scripts/master_doc_cover.py | 348 ++++++++++++++++++ .../tester/helper_836_1_machine_cover.cjs | 63 ++++ resources/tester/test_836_1_machine_cover.py | 336 +++++++++++++++++ 7 files changed, 1085 insertions(+), 9 deletions(-) create mode 100644 common_util/common_util_formula_doc_cover.ts create mode 100644 resources/master_data/scripts/master_doc_cover.py create mode 100644 resources/tester/helper_836_1_machine_cover.cjs create mode 100644 resources/tester/test_836_1_machine_cover.py diff --git a/M02_MasterTemplete/M02_Doc_Sk_Path.py b/M02_MasterTemplete/M02_Doc_Sk_Path.py index e740a5101..b662f415c 100644 --- a/M02_MasterTemplete/M02_Doc_Sk_Path.py +++ b/M02_MasterTemplete/M02_Doc_Sk_Path.py @@ -26,6 +26,8 @@ from M02_MasterTemplete import M02_MyMachine_Draft as draft from M02_MasterTemplete import M02_Sangeun_Copy as skc from M02_MasterTemplete import M02_Sangeun_Doc as sd +import master_doc_cover as mcv # noqa: E402 — 기계 덮음(836-1 · scripts 길 = dc) + MAX = 2_000_000 # 문서 글 크기(바이트) 상한 JOBS = (skc.PRICE, skc.REGEN) @@ -133,8 +135,19 @@ def edited(copy: dict, body: dict, key: str, sk: dict | None = None) -> dict: *dc.calls(out["문서"]), *(c for d in out.get("끊음", {}).values() for c in dc.calls(d)), ] + try: # 기계 덮음(836-1) — 몸 검사 · 원본과 같은 칸 · 안 부르는 기계 뺌(가져온 식 = 검사만) + if dim.is_imported(sk): + got = mcv.clean(out["문서"].get(mcv.COVER)) + rest = {k: v for k, v in out["문서"].items() if k != mcv.COVER} + out["문서"] = {**rest, mcv.COVER: got} if got else rest + except ValueError as e: + raise _bad(str(e)) from e if not dim.is_imported(sk): out["부름"] = dc.closure(dc.over(out), calls, copy.get("부름")) + try: + out["문서"] = mcv.prune_doc(out["문서"], dc.ml.Links(dc.over(out), dc.docs_of(out))) + except ValueError as e: + raise _bad(str(e)) from e out = gap.fill(out) # 셈이 읽은 줄 가운데 사본 없는 것 = 지금 값으로 박음(822-1) out = draft.renew(out) # 기계 창 초안으로 만든 · 고친 내 기계 = 새 값(827-1) out["따로"] = True diff --git a/common_util/common_util_formula_doc_calc.ts b/common_util/common_util_formula_doc_calc.ts index 5aafeccab..fe33fc79d 100644 --- a/common_util/common_util_formula_doc_calc.ts +++ b/common_util/common_util_formula_doc_calc.ts @@ -25,6 +25,7 @@ import { type Node, type Rule, } from "./common_util_formula_doc"; +import { coverDoc, coverOf as machineCover, type Cover } from "./common_util_formula_doc_cover"; const EPS = 1e-7; // B09_Estimation_Calc_Base EPS 와 같음 const DEEP = 20; @@ -137,6 +138,7 @@ class Run { readonly given: Record, readonly stack: string[], readonly memo: Memo, + readonly cover: Cover, // 기계 덮음(836-1) — 맨 위 문서 칸 · 부름에 얹음 ) { for (const v of doc.변수 ?? []) this.vars.set(varId(v), v); for (const l of doc.식 ?? []) this.lines.set(String(l.기호 ?? ""), l.식); @@ -364,8 +366,13 @@ class Run { .sort() .map((k) => `${k}=${plainText(given[k])}`) .join("|")}`; - if (!this.memo.has(memoKey)) - this.memo.set(memoKey, calcDoc(doc, this.links, given, [...this.stack, key], this.memo)); + if (!this.memo.has(memoKey)) { + const shaped = coverDoc(doc, given, this.cover); + this.memo.set( + memoKey, + calcDoc(shaped, this.links, given, [...this.stack, key], this.memo, this.cover), + ); + } const got = this.memo.get(memoKey) as DocAnswer; if (!got.셈됨) { // 값 없음(결과 빈 입력 · 표 줄 없음) = 빈 값 · 그 밖(표지) = 멈춤 @@ -565,16 +572,17 @@ export function rulesOf(doc: FormulaDoc): Record { const STOP_KINDS = ["기호 꼴", "연결 꼴", "금액 줄", "끝수", "겹침"]; -/** 수식 문서 셈 — given = {변수 열쇠: 값}(단가산출 부름이 넘긴 입력) · 예외 없음 */ +/** 수식 문서 셈 — given = {변수 열쇠: 값}(단가산출 부름이 넘긴 입력) · cover = 기계 덮음(없음 = 문서 칸) · 예외 없음 */ export function calcDoc( doc: FormulaDoc, links: DocLinks, given: Record = {}, stack: string[] = [], memo: Memo = new Map(), + cover?: Cover, ): DocAnswer { if (!stack.length && doc.키) stack = [String(doc.키)]; // 제 키 — 저를 부르면 순환 - const run = new Run(doc, links, given, stack, memo); + const run = new Run(doc, links, given, stack, memo, cover ?? machineCover(doc)); for (const one of check(doc)) if (STOP_KINDS.includes(one.갈래)) run.marks.push(one); const rules = rulesOf(doc); for (const name of [...run.lines.keys(), ...run.vars.keys()]) if (name) run.get(name); diff --git a/common_util/common_util_formula_doc_cover.ts b/common_util/common_util_formula_doc_cover.ts new file mode 100644 index 000000000..b1093e794 --- /dev/null +++ b/common_util/common_util_formula_doc_cover.ts @@ -0,0 +1,298 @@ +/* ============================================================================= + * common_util_formula_doc_cover.ts + * 기계 덮음(PLAN 836-1 · 요구 846 · 849 · 850 · 853) — 단가산출 안 기계 내부 값 고침(사본 줄은 그대로 · 고친 것만 따로) + * 자리 = 문서 칸 `기계덮음` {기계 입력 글: {값?: {칸: 값}, 조종원?: [{키, 인}], 잡품?: [{줄? | 키?, 이름?, 수량?, 율}]}} + * 기계 입력 글 = 부름 입력 `기계` 값(따옴표 뗌 · 원문번호 · 개인 EQP…) — 같은 기계 줄 여럿 = 한 벌 + * 값 = 기계 식이 읽는 기계 칸(요소 `EQ:{S_기계}` 칸 — 조종원 · 조종원직종 · 잡재료율 빼고) · 조종원 = 인력 줄(노임 키 · 인/일) · + * 잡품 = 재료 줄(율 % · 줄 = 그 금액 줄 단가 · 수량 따름(기본) · 키 = 고른 재료 요소) · 원본과 같은 칸 = 없음(`prune`) + * 셈 규칙(`coverDoc`) = 계산기가 기계 식을 부를 때 그 기계 덮음을 얹은 문서로 셈 — 덮음 없음 = 문서 그대로(금액 전후 같음) + * 파이썬 · TS 짝 — 짝 = resources/master_data/scripts/master_doc_cover.py · 거울 시험 test_836_1_machine_cover.py + * · 화면 · DOM · fetch 없음 + * ========================================================================== */ + +import { + names, + parse, + symId, + varId, + type DocVar, + type FormulaDoc, + type MoneyLine, +} from "./common_util_formula_doc"; + +export const COVER = "기계덮음"; +/** 값 칸에서 빼는 기계 칸 — 줄(조종원 · 잡품)로 다룸 */ +export const NOT_VALUE = ["조종원", "조종원직종", "잡재료율"]; +const HOUR = "Q_시간환산"; +/** 조종원 줄을 새로 넣을 때(조종원 없는 식) 시간환산 — GC000267 과 같은 꼴(건설품셈 공통 1-1-3 2) */ +const HOUR_VARS: DocVar[] = [ + { + 열쇠: "Q_분자^표", 기호: "Q", 아래: "분자", 위: "표", 이름: "건설기계 조종원 노임 시간 환산 계수.분자", 단위: "", + 연결: { 갈래: "표", 표: "CC000336", 조건: { 구분: "'시간환산'" }, 칸: "분자" }, + }, + { + 열쇠: "Q_분모^표", 기호: "Q", 아래: "분모", 위: "표", 이름: "건설기계 조종원 노임 시간 환산 계수.분모", 단위: "", + 연결: { 갈래: "표", 표: "CC000336", 조건: { 구분: "'시간환산'" }, 칸: "분모" }, + }, + { 열쇠: "시간환산", 기호: "Q", 아래: "시간환산", 위: "", 이름: "시간환산", 단위: "", 연결: { 갈래: "식" } }, +]; // prettier-ignore +const HOUR_LINE = { 기호: HOUR, 식: "Q_분자^표 ÷ Q_분모^표" }; + +export interface CrewRow { + 키: string; + 인: number; +} +export interface MiscRow { + 줄?: string; + 키?: string; + 이름?: string; + 단위?: string; + 수량?: number; + 율: number; +} +export interface MachineCover { + 값?: Record; + 조종원?: CrewRow[]; + 잡품?: MiscRow[]; +} +export type Cover = Record; +type Holder = FormulaDoc & { [COVER]?: Cover }; + +const clone = (v: T): T => JSON.parse(JSON.stringify(v)) as T; +const plain = (n: number): string => String(Number(n.toPrecision(15))); + +export const coverOf = (doc: FormulaDoc | null | undefined): Cover => { + const got = (doc as Holder | null | undefined)?.[COVER]; + return got && typeof got === "object" && !Array.isArray(got) ? got : {}; +}; + +/** 부름 입력 → 기계 열쇠(따옴표 뗌) · 기계 입력 없음 = "" */ +export function machineKey(given: Record | null | undefined): string { + const v = given?.기계; + if (v === undefined || v === null) return ""; + const t = String(v).trim(); + return /^'.*'$/.test(t) ? t.slice(1, -1) : t; +} + +const namesOf = (text: unknown): string[] => { + if (text === null || text === undefined || String(text).trim() === "") return []; + try { + return names(parse(text)); + } catch { + return []; + } +}; + +export interface CoverParts { + /** 값 칸 — 칸 이름 · 그 칸을 읽는 기호(차례 = 문서 변수 차례) */ + fields: { 칸: string; ids: string[] }[]; + /** 조종원 금액 줄 차례 · 사람 수 기호 */ + crew: number[]; + crewQty: Record; + /** 잡품(잡재료비) 금액 줄 차례 · 율 기호 · 밑 재료 줄 차례 */ + misc: number | null; + rate: string | null; + base: number[]; +} + +/** 기계 식 짜임 — 기계 칸 요소(`EQ:…{S_기계}…`) · 조종원 줄(노무비 · 수량이 칸 「조종원」) · 잡품 줄(재료비 누계 × 율) */ +export function partsOf(doc: FormulaDoc): CoverParts { + const eq = new Map(); + for (const v of doc.변수 ?? []) { + const l = v.연결 ?? {}; + const k = String(l.키 ?? ""); + if (l.갈래 === "요소" && k.startsWith("EQ:") && k.includes("{S_기계}") && l.칸) + eq.set(varId(v), l.칸); + } + const fields: CoverParts["fields"] = []; + for (const [id, col] of eq) { + if (NOT_VALUE.includes(col)) continue; + const at = fields.find((f) => f.칸 === col); + if (at) at.ids.push(id); + else fields.push({ 칸: col, ids: [id] }); + } + const lines = doc.금액 ?? []; + const crew: number[] = []; + const crewQty: Record = {}; + let misc: number | null = null; + let rate: string | null = null; + lines.forEach((l, i) => { + const q = namesOf(l.수량).find((n) => eq.get(n) === "조종원"); + if (l.비목 === "노무비" && q) { + crew.push(i); + crewQty[i] = q; + } + const p = namesOf(l.단가); + const other = p.filter((n) => n !== "재료비"); + if (misc === null && l.비목 === "재료비" && p.includes("재료비") && other.length === 1) { + misc = i; + rate = other[0]; + } + }); + const base = + misc === null + ? [] + : lines.flatMap((l, i) => (i < misc! && l.비목 === "재료비" && !l.합밖 ? [i] : [])); + return { fields, crew, crewQty, misc, rate, base }; +} + +type Vals = Record; +const num = (v: unknown): number | null => (typeof v === "number" && Number.isFinite(v) ? v : null); +/** 요소 키 글의 구멍을 값으로 채움(`LB:{S_직종}` → `LB:1048`) */ +const fillKey = (text: string, vals: Vals): string => + text.replace(/\{([^{}]+)\}/g, (_, h: string) => { + const v = vals[h]; + return typeof v === "number" ? plain(v) : String(v ?? ""); + }); + +/** 원본(덮음 없음) 기본값 — 셈 답 값(`vals` = 그 기계로 셈한 원본 답 `값`) */ +export function defaultsOf(doc: FormulaDoc, vals: Vals): Required { + const parts = partsOf(doc); + const lines = doc.금액 ?? []; + const vars = new Map((doc.변수 ?? []).map((v) => [varId(v), v])); + const 값: Record = {}; + for (const f of parts.fields) { + const v = vals[f.ids[0]]; + if (typeof v === "number" || typeof v === "string") 값[f.칸] = v; + } + const 조종원 = parts.crew.flatMap((i) => { + const n = num(vals[parts.crewQty[i]]); + const sym = namesOf(lines[i].단가)[0]; + const key = String(vars.get(sym ?? "")?.연결?.키 ?? ""); + return n && key ? [{ 키: fillKey(key, vals), 인: n }] : []; + }); + const r = parts.rate ? num(vals[parts.rate]) : null; + const 잡품 = r ? parts.base.map((i) => ({ 줄: String(lines[i].이름 ?? ""), 율: r })) : []; + return { 값, 조종원, 잡품 }; +} + +const canon = (v: unknown): string => + JSON.stringify(v, (_, x: unknown) => + x && typeof x === "object" && !Array.isArray(x) + ? Object.fromEntries( + Object.entries(x as Record) + .filter(([, y]) => y !== undefined && y !== null && y !== "") + .sort(([a], [b]) => (a < b ? -1 : 1)), + ) + : typeof x === "number" + ? Number(x.toPrecision(12)) + : x, + ); +const sameVal = (a: unknown, b: unknown): boolean => + typeof a === "number" && typeof b === "number" + ? Math.abs(a - b) <= 1e-9 * Math.max(1, Math.abs(a), Math.abs(b)) + : String(a) === String(b); + +/** 원본과 같은 칸 · 줄을 뺀 덮음(바꿨다 되돌림 = 안 고침) · 다 빠지면 null */ +export function prune(entry: MachineCover, base: Required): MachineCover | null { + const out: MachineCover = {}; + const vals = Object.entries(entry.값 ?? {}).filter( + ([k, v]) => + v !== null && v !== undefined && v !== "" && !(k in base.값 && sameVal(v, base.값[k])), + ); + if (vals.length) out.값 = Object.fromEntries(vals); + if (entry.조종원 && canon(entry.조종원) !== canon(base.조종원)) out.조종원 = entry.조종원; + if (entry.잡품 && canon(entry.잡품) !== canon(base.잡품)) out.잡품 = entry.잡품; + return Object.keys(out).length ? out : null; +} + +/** 덮음 한 기계 고침 → 문서 칸(원본 대조로 정리 · 비면 칸 지움) */ +export function putCover( + doc: FormulaDoc, + machine: string, + entry: MachineCover, + base: Required, +): void { + const box = doc as Holder; + const all = { ...coverOf(doc) }; + const kept = prune(entry, base); + if (kept) all[machine] = kept; + else delete all[machine]; + if (Object.keys(all).length) box[COVER] = all; + else delete box[COVER]; +} + +// ── 셈 규칙 ─────────────────────────────────────────────────────────── +/** 새 요소 기호(`w_조종원1` · `P_잡품1` — 문서에 없는 첫 번호) */ +function addVar(doc: FormulaDoc, sign: string, low: string, name: string, key: string): string { + const have = new Set([ + ...(doc.변수 ?? []).map(varId), + ...(doc.식 ?? []).map((l) => String(l.기호 ?? "")), + ]); + let n = 1; + while (have.has(symId(sign, `${low}${n}`))) n++; + const 아래 = `${low}${n}`; + const id = symId(sign, 아래); + (doc.변수 ??= []).push({ + 열쇠: id, 기호: sign, 아래, 위: "", 이름: name, 단위: "", 연결: { 갈래: "요소", 키: key }, + }); // prettier-ignore + return id; +} + +function crewLines(doc: FormulaDoc, rows: CrewRow[]): MoneyLine[] { + const have = (doc.변수 ?? []).some((v) => varId(v) === HOUR) || (doc.식 ?? []).some((l) => l.기호 === HOUR); // prettier-ignore + if (rows.length && !have) { + const ids = new Set((doc.변수 ?? []).map(varId)); + for (const v of HOUR_VARS) if (!ids.has(varId(v))) (doc.변수 ??= []).push(clone(v)); + (doc.식 ??= []).push({ ...HOUR_LINE }); + } + return rows.map((r) => ({ + 이름: "조종원", + 단위: "인", + 비목: "노무비", + 수량: `${plain(r.인)} × ${HOUR}`, + 단가: addVar(doc, "w", "조종원", "조종원 노임", r.키), + })); +} + +function miscLines(doc: FormulaDoc, rows: MiscRow[], lines: MoneyLine[]): MoneyLine[] { + return rows.flatMap((r) => { + let price: string; + let qty: string; + let label = r.이름 ?? ""; + if (r.키) { + price = addVar(doc, "P", "잡품", label || r.키, r.키); + qty = plain(r.수량 ?? 1); + } else { + const b = lines.find((l) => String(l.이름 ?? "") === r.줄); + if (!b || !b.단가) return []; + price = String(b.단가); + qty = r.수량 !== undefined && r.수량 !== null ? plain(r.수량) : `(${b.수량 ?? 1})`; + label ||= String(b.이름 ?? ""); + } + return [{ 이름: "잡품", 규격: label, 단위: "", 비목: "재료비", 수량: `${qty} × ${plain(r.율)} ÷ 100`, 단가: price } as MoneyLine]; // prettier-ignore + }); +} + +/** 기계 식 문서에 그 기계 덮음을 얹음(새 문서) — 덮음 없음 = 그대로(같은 물건) */ +export function coverDoc( + doc: FormulaDoc, + given: Record | null | undefined, + cover: Cover | null | undefined, +): FormulaDoc { + const entry = cover?.[machineKey(given)]; + if (!entry || !machineKey(given)) return doc; + const out = clone(doc) as FormulaDoc & { 쌓기?: unknown }; + delete out.쌓기; // 금액 차례가 바뀜 — 기본 차림 + const parts = partsOf(out); + for (const f of parts.fields) + if (entry.값 && f.칸 in entry.값) + for (const v of out.변수 ?? []) + if (f.ids.includes(varId(v))) v.연결 = { 갈래: "숫자", 값: entry.값[f.칸] }; + const lines = out.금액 ?? []; + const crew = entry.조종원 ? crewLines(out, entry.조종원) : null; + const misc = entry.잡품 && (parts.misc !== null || entry.잡품.length) ? miscLines(out, entry.잡품, lines) : null; // prettier-ignore + const gone = new Set([...(crew ? parts.crew : []), ...(misc && parts.misc !== null ? [parts.misc] : [])]); // prettier-ignore + const tail = lines.findIndex((l) => l.비목 === "경비"); + const end = tail < 0 ? lines.length : tail; + const crewAt = parts.crew.length ? parts.crew[0] : end; + const miscAt = parts.misc ?? end; + const next: MoneyLine[] = []; + for (let i = 0; i <= lines.length; i++) { + if (crew && i === crewAt) next.push(...crew); + if (misc && i === miscAt) next.push(...misc); + if (i < lines.length && !gone.has(i)) next.push(lines[i]); + } + out.금액 = next; + return out; +} diff --git a/resources/master_data/scripts/master_doc_calc.py b/resources/master_data/scripts/master_doc_calc.py index 6f0b661b3..8c13f2f2d 100644 --- a/resources/master_data/scripts/master_doc_calc.py +++ b/resources/master_data/scripts/master_doc_calc.py @@ -16,6 +16,7 @@ import math from decimal import ROUND_DOWN, ROUND_HALF_UP, ROUND_UP, Decimal, InvalidOperation import master_doc as md +import master_doc_cover as mcv COSTS, BUNDLE = md.COSTS, md.BUNDLE EPS = 1e-7 # B09_Estimation_Calc_Base.EPS 와 같음 @@ -79,8 +80,9 @@ def number(v): # ── 셈 한 벌 ────────────────────────────────────────────────────────── class _Run: - def __init__(self, doc: dict, links, given: dict | None, stack: tuple, memo: dict): + def __init__(self, doc: dict, links, given: dict | None, stack: tuple, memo: dict, cover=None): self.doc, self.links, self.stack, self.memo = doc, links, stack, memo + self.cover = cover # 기계 덮음(836-1) — 맨 위 문서 칸 · 부름에 얹음 self.given = given or {} self.vars = {md.var_id(v): v for v in doc.get("변수") or []} self.lines = {str(x.get("기호") or ""): x for x in doc.get("식") or []} @@ -296,7 +298,10 @@ class _Run: given = payload.get("입력") or {} memo_key = (key, tuple(sorted((k, plain_text(v)) for k, v in given.items()))) if memo_key not in self.memo: - self.memo[memo_key] = calc(doc, self.links, given, (*self.stack, key), self.memo) + doc = mcv.cover_doc(doc, given, self.cover) + self.memo[memo_key] = calc( + doc, self.links, given, (*self.stack, key), self.memo, self.cover + ) got = self.memo[memo_key] if not got["셈됨"]: # 값 없음(결과 빈 입력 · 표 줄 없음) = 빈 값 · 그 밖(표지) = 멈춤 why = got.get("까닭") or " · ".join(m["글"] for m in got["표지"]) @@ -505,14 +510,19 @@ def rules_of(doc: dict) -> dict: def calc( - doc: dict, links, given: dict | None = None, stack: tuple = (), memo: dict | None = None + doc: dict, + links, + given: dict | None = None, + stack: tuple = (), + memo: dict | None = None, + cover: dict | None = None, ) -> dict: """수식 문서 셈 → {값, 빈값, 줄, 노무비 · 재료비 · 경비 · 계 | 결과, 묶음, 머리, 단계?, 표지, 셈됨, 끝수}. - given = {변수 열쇠: 값}(단가산출 부름이 넘긴 입력) · 예외 없음.""" + given = {변수 열쇠: 값}(단가산출 부름이 넘긴 입력) · cover = 기계 덮음(없음 = 문서 칸) · 예외 없음.""" memo = {} if memo is None else memo stack = stack or ((str(doc["키"]),) if doc.get("키") else ()) # 제 키 — 저를 부르면 순환 - run = _Run(doc, links, given, stack, memo) + run = _Run(doc, links, given, stack, memo, mcv.cover_of(doc) if cover is None else cover) for one in md.check(doc): if one["갈래"] in ("기호 꼴", "연결 꼴", "금액 줄", "끝수", "겹침"): run.marks.append(one) diff --git a/resources/master_data/scripts/master_doc_cover.py b/resources/master_data/scripts/master_doc_cover.py new file mode 100644 index 000000000..b7fe9b260 --- /dev/null +++ b/resources/master_data/scripts/master_doc_cover.py @@ -0,0 +1,348 @@ +# -*- coding: utf-8 -*- +"""기계 덮음 (PLAN 836-1 · 요구 846 · 849 · 850 · 853) — 단가산출 안 기계 내부 값 고침. + +자리 = 문서 칸 `기계덮음` {기계 입력 글: {값?: {칸: 값}, 조종원?: [{키, 인}], 잡품?: [{줄? | 키?, 이름?, 수량?, 율}]}} + 기계 입력 글 = 부름 입력 `기계` 값(따옴표 뗌) — 같은 기계 줄 여럿 = 한 벌 · 원본과 같은 칸 = 없음(`prune`). +셈 규칙(`cover_doc`) = 계산기(`master_doc_calc.calc`)가 기계 식을 부를 때 그 기계 덮음을 얹은 문서로 셈 — + 덮음 없음 = 문서 그대로(금액 전후 같음). +파이썬 · TS 짝 — 짝 = `common_util/common_util_formula_doc_cover.ts` · 거울 시험 +`resources/tester/test_836_1_machine_cover.py`. +""" + +from __future__ import annotations + +import copy as cp +import json +import re +from decimal import Decimal + +import master_doc as md + +COVER = "기계덮음" +NOT_VALUE = ( + "조종원", + "조종원직종", + "잡재료율", +) # 값 칸에서 빼는 기계 칸 — 줄(조종원 · 잡품)로 다룸 +HOUR = "Q_시간환산" +_HOUR_VARS = [ + {"열쇠": "Q_분자^표", "기호": "Q", "아래": "분자", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", + "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분자"}}, + {"열쇠": "Q_분모^표", "기호": "Q", "아래": "분모", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", + "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분모"}}, + {"열쇠": "시간환산", "기호": "Q", "아래": "시간환산", "위": "", "이름": "시간환산", "단위": "", "연결": {"갈래": "식"}}, +] # fmt: skip +_HOUR_LINE = {"기호": HOUR, "식": "Q_분자^표 ÷ Q_분모^표"} +MAX_ROWS = 30 + + +def _plain(n) -> str: + d = Decimal(str(n)).normalize() + return format(d, "f") + + +def cover_of(doc) -> dict: + got = doc.get(COVER) if isinstance(doc, dict) else None + return got if isinstance(got, dict) else {} + + +def machine_key(given) -> str: + """부름 입력 → 기계 열쇠(따옴표 뗌) · 기계 입력 없음 = "".""" + v = (given or {}).get("기계") + if v is None: + return "" + t = str(v).strip() + return t[1:-1] if len(t) >= 2 and t[0] == "'" and t[-1] == "'" else t + + +def _names(text) -> list[str]: + if text is None or str(text).strip() == "": + return [] + try: + return md.names(md.parse(text)) + except md.DocError: + return [] + + +def parts_of(doc: dict) -> dict: + """기계 식 짜임 — {fields [{칸, ids}], crew [줄 차례], crew_qty {차례: 기호}, misc, rate, base}.""" + eq: dict[str, str] = {} + for v in doc.get("변수") or []: + link = v.get("연결") or {} + key = str(link.get("키") or "") + if ( + link.get("갈래") == "요소" + and key.startswith("EQ:") + and "{S_기계}" in key + and link.get("칸") + ): + eq[md.var_id(v)] = link["칸"] + fields: list[dict] = [] + for id_, col in eq.items(): + if col in NOT_VALUE: + continue + at = next((f for f in fields if f["칸"] == col), None) + if at: + at["ids"].append(id_) + else: + fields.append({"칸": col, "ids": [id_]}) + lines = doc.get("금액") or [] + crew, crew_qty, misc, rate = [], {}, None, None + for i, line in enumerate(lines): + q = next((n for n in _names(line.get("수량")) if eq.get(n) == "조종원"), None) + if line.get("비목") == "노무비" and q: + crew.append(i) + crew_qty[i] = q + p = _names(line.get("단가")) + other = [n for n in p if n != "재료비"] + if misc is None and line.get("비목") == "재료비" and "재료비" in p and len(other) == 1: + misc, rate = i, other[0] + base = ( + [] + if misc is None + else [ + i + for i, x in enumerate(lines) + if i < misc and x.get("비목") == "재료비" and not x.get("합밖") + ] + ) + return {"fields": fields, "crew": crew, "crew_qty": crew_qty, "misc": misc, "rate": rate, "base": base} # fmt: skip + + +def _num(v): + if isinstance(v, bool) or not isinstance(v, (int, float, Decimal)): + return None + return v + + +def _fill_key(text: str, vals: dict) -> str: + def one(m): + v = vals.get(m.group(1)) + return _plain(v) if _num(v) is not None else str("" if v is None else v) + + return re.sub(r"\{([^{}]+)\}", one, text) + + +def defaults_of(doc: dict, vals: dict) -> dict: + """원본(덮음 없음) 기본값 — `vals` = 그 기계로 셈한 원본 답 `값`.""" + parts = parts_of(doc) + lines = doc.get("금액") or [] + vars_ = {md.var_id(v): v for v in doc.get("변수") or []} + values = {} + for f in parts["fields"]: + v = vals.get(f["ids"][0]) + if _num(v) is not None or isinstance(v, str): + values[f["칸"]] = v + crew = [] + for i in parts["crew"]: + n = _num(vals.get(parts["crew_qty"][i])) + sym = (_names(lines[i].get("단가")) or [""])[0] + key = str(((vars_.get(sym) or {}).get("연결") or {}).get("키") or "") + if n and key: + crew.append({"키": _fill_key(key, vals), "인": n}) + r = _num(vals.get(parts["rate"])) if parts["rate"] else None + misc = [{"줄": str(lines[i].get("이름") or ""), "율": r} for i in parts["base"]] if r else [] + return {"값": values, "조종원": crew, "잡품": misc} + + +def _canon(v) -> str: + def norm(x): + if isinstance(x, dict): + return {k: norm(y) for k, y in sorted(x.items()) if y not in (None, "")} + if isinstance(x, list): + return [norm(y) for y in x] + if _num(x) is not None: + return float(f"{float(x):.12g}") + return x + + return json.dumps(norm(v), ensure_ascii=False, sort_keys=True) + + +def _same(a, b) -> bool: + if _num(a) is not None and _num(b) is not None: + a, b = float(a), float(b) + return abs(a - b) <= 1e-9 * max(1.0, abs(a), abs(b)) + return str(a) == str(b) + + +def prune(entry: dict, base: dict) -> dict | None: + """원본과 같은 칸 · 줄을 뺀 덮음(바꿨다 되돌림 = 안 고침) · 다 빠지면 None.""" + out: dict = {} + vals = { + k: v + for k, v in (entry.get("값") or {}).items() + if v not in (None, "") and not (k in base["값"] and _same(v, base["값"][k])) + } + if vals: + out["값"] = vals + for part in ("조종원", "잡품"): + if entry.get(part) is not None and _canon(entry[part]) != _canon(base[part]): + out[part] = entry[part] + return out or None + + +# ── 셈 규칙 ─────────────────────────────────────────────────────────── +def _add_var(doc: dict, sign: str, low: str, name: str, key: str) -> str: + have = {md.var_id(v) for v in doc.get("변수") or []} | { + str(x.get("기호") or "") for x in doc.get("식") or [] + } + n = 1 + while md.sym_id(sign, f"{low}{n}") in have: + n += 1 + under = f"{low}{n}" + id_ = md.sym_id(sign, under) + doc.setdefault("변수", []).append( + {"열쇠": id_, "기호": sign, "아래": under, "위": "", "이름": name, "단위": "", + "연결": {"갈래": "요소", "키": key}} + ) # fmt: skip + return id_ + + +def _crew_lines(doc: dict, rows: list) -> list[dict]: + have = any(md.var_id(v) == HOUR for v in doc.get("변수") or []) or any( + x.get("기호") == HOUR for x in doc.get("식") or [] + ) + if rows and not have: + ids = {md.var_id(v) for v in doc.get("변수") or []} + doc.setdefault("변수", []).extend( + cp.deepcopy(v) for v in _HOUR_VARS if md.var_id(v) not in ids + ) + doc.setdefault("식", []).append(dict(_HOUR_LINE)) + return [ + {"이름": "조종원", "단위": "인", "비목": "노무비", "수량": f"{_plain(r['인'])} × {HOUR}", + "단가": _add_var(doc, "w", "조종원", "조종원 노임", r["키"])} + for r in rows + ] # fmt: skip + + +def _misc_lines(doc: dict, rows: list, lines: list) -> list[dict]: + out = [] + for r in rows: + label = r.get("이름") or "" + if r.get("키"): + price = _add_var(doc, "P", "잡품", label or r["키"], r["키"]) + qty = _plain(r["수량"] if r.get("수량") is not None else 1) + else: + b = next((x for x in lines if str(x.get("이름") or "") == r.get("줄")), None) + if not b or not b.get("단가"): + continue + price = str(b["단가"]) + qty = _plain(r["수량"]) if r.get("수량") is not None else f"({b.get('수량') or 1})" + label = label or str(b.get("이름") or "") + out.append( + {"이름": "잡품", "규격": label, "단위": "", "비목": "재료비", + "수량": f"{qty} × {_plain(r['율'])} ÷ 100", "단가": price} + ) # fmt: skip + return out + + +def cover_doc(doc: dict, given, cover) -> dict: + """기계 식 문서에 그 기계 덮음을 얹음(새 문서) — 덮음 없음 = 그대로(같은 물건).""" + key = machine_key(given) + entry = (cover or {}).get(key) if key else None + if not entry: + return doc + out = cp.deepcopy(doc) + out.pop("쌓기", None) # 금액 차례가 바뀜 — 기본 차림 + parts = parts_of(out) + values = entry.get("값") or {} + for f in parts["fields"]: + if f["칸"] in values: + for v in out.get("변수") or []: + if md.var_id(v) in f["ids"]: + v["연결"] = {"갈래": "숫자", "값": values[f["칸"]]} + lines = out.get("금액") or [] + crew = _crew_lines(out, entry["조종원"]) if entry.get("조종원") is not None else None + want = entry.get("잡품") + misc = ( + _misc_lines(out, want, lines) + if want is not None and (parts["misc"] is not None or want) + else None + ) + gone = set(parts["crew"] if crew is not None else []) | ( + {parts["misc"]} if misc is not None and parts["misc"] is not None else set() + ) + tail = next((i for i, x in enumerate(lines) if x.get("비목") == "경비"), len(lines)) + crew_at = parts["crew"][0] if parts["crew"] else tail + misc_at = parts["misc"] if parts["misc"] is not None else tail + new = [] + for i in range(len(lines) + 1): + if crew is not None and i == crew_at: + new += crew + if misc is not None and i == misc_at: + new += misc + if i < len(lines) and i not in gone: + new.append(lines[i]) + out["금액"] = new + return out + + +# ── 서버 검사 · 정리 ────────────────────────────────────────────────── +def _nonneg(v, where: str): + if isinstance(v, bool) or not isinstance(v, (int, float, Decimal)) or v < 0: + raise ValueError(f"{where} — 0 이상 수") + return v + + +def clean(raw) -> dict: + """몸 검사 — 틀리면 ValueError(까닭) · 빈 칸 · 빈 기계 = 뺌.""" + if raw is None: + return {} + if not isinstance(raw, dict): + raise ValueError(f"{COVER} — 묶음 아님") + out: dict = {} + for key, entry in raw.items(): + if not isinstance(entry, dict): + raise ValueError(f"{COVER} 「{key}」 — 묶음 아님") + one: dict = {} + values = entry.get("값") + if values is not None: + if not isinstance(values, dict): + raise ValueError(f"{COVER} 「{key}」 값 — 묶음 아님") + got = {} + for col, v in values.items(): + if v in (None, ""): + continue + if col in NOT_VALUE: + raise ValueError(f"{COVER} 「{key}」 값 「{col}」 — 줄로 고침") + got[col] = v.strip() if isinstance(v, str) else _nonneg(v, f"「{key}」 {col}") + if got: + one["값"] = got + for part, need in (("조종원", ("키", "인")), ("잡품", ("율",))): + rows = entry.get(part) + if rows is None: + continue + if not isinstance(rows, list) or len(rows) > MAX_ROWS: + raise ValueError(f"{COVER} 「{key}」 {part} — 목록({MAX_ROWS} 줄까지)") + for r in rows: + if not isinstance(r, dict) or any(r.get(k) in (None, "") for k in need): + raise ValueError(f"{COVER} 「{key}」 {part} 줄 — {' · '.join(need)} 필요") + for k in ("인", "율", "수량"): + if r.get(k) is not None: + _nonneg(r[k], f"「{key}」 {part} {k}") + if part == "잡품" and not (r.get("줄") or r.get("키")): + raise ValueError(f"{COVER} 「{key}」 잡품 줄 — 재료(키) 또는 줄 필요") + one[part] = rows + if one: + out[str(key)] = one + return out + + +def prune_doc(doc: dict, links) -> dict: + """서버 정리 — 덮음을 검사하고 원본과 같은 칸 · 줄 · 기계를 뺀 문서(새 문서 · 덮음 없음 = 그대로). + 원본 = 덮음 뺀 문서 셈의 부름 메모(그 기계 식 · 입력 · 원본 답).""" + import master_doc_calc as mc + + cover = clean(doc.get(COVER)) + rest = {k: v for k, v in doc.items() if k != COVER} + if not cover: + return rest + memo: dict = {} + mc.calc(rest, links, None, (), memo) + kept: dict = {} # 문서가 안 부르는 기계 덮음 = 뺌 + for (key, given), ans in memo.items(): + m = machine_key(dict(given)) + sub = links.doc(key) if m in cover and m not in kept else None + if sub and (one := prune(cover[m], defaults_of(sub, ans.get("값") or {}))): + kept[m] = one + return {**rest, COVER: kept} if kept else rest diff --git a/resources/tester/helper_836_1_machine_cover.cjs b/resources/tester/helper_836_1_machine_cover.cjs new file mode 100644 index 000000000..3b4c13d28 --- /dev/null +++ b/resources/tester/helper_836_1_machine_cover.cjs @@ -0,0 +1,63 @@ +/* 836-1 기계 덮음 거울 헬퍼 — common_util_formula_doc_cover.ts · calc 를 그 자리에서 트랜스파일해 Node 로 돌림. + * stdin = {문서들: {키: 문서}, 대상: [{문서, 덮음?}], 기록: {열쇠: {값 | 빈 | 끊김}}, 짜임: [{문서, 입력, 덮음, 값}]} + * stdout = {답: [calcDoc 답], 짜임: [{얹음: coverDoc, 기본: defaultsOf, 정리: prune}]} + * 기록 열쇠 = 갈래 + "|" + 값 글(파이썬 `_canon` 과 같은 꼴 · helper_802_1 과 같음). */ +const fs = require("fs"); +const path = require("path"); +const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); + +const cache = new Map(); +function load(at) { + if (cache.has(at)) return cache.get(at); + const js = ts.transpileModule(fs.readFileSync(at, "utf8"), { + compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, + }).outputText; + const box = { exports: {} }; + cache.set(at, box.exports); + const need = (p) => + p.startsWith(".") ? load(path.resolve(path.dirname(at), `${p}.ts`)) : require(p); + new Function("exports", "module", "require", js)(box.exports, box, need); + return box.exports; +} +const root = path.join(__dirname, "..", "..", "common_util"); +const calc = load(path.join(root, "common_util_formula_doc_calc.ts")); +const cover = load(path.join(root, "common_util_formula_doc_cover.ts")); + +function canon(v) { + if (typeof v === "number") return calc.plainText(v); + if (Array.isArray(v)) return v.map(canon); + if (v && typeof v === "object") { + const out = {}; + for (const k of Object.keys(v).sort()) out[k] = canon(v[k]); + return out; + } + return v; +} +const input = JSON.parse(fs.readFileSync(0, "utf8")); +const record = input.기록 || {}; +function answer(kind, p) { + const k = `${kind}|${JSON.stringify(canon(p))}`; + if (!(k in record)) throw new calc.Broken(`기록 없음 ${k}`); + const got = record[k]; + if ("빈" in got) throw new calc.Gap(got.빈); + if ("끊김" in got) throw new calc.Broken(got.끊김); + return got.값; +} +const links = { + element: (p) => answer("요소", p), + table: (p) => answer("표", p), + doc: (key) => (input.문서들 || {})[key] || null, +}; +const out = { 답: [], 짜임: [] }; +for (const one of input.대상 || []) + out.답.push(calc.calcDoc(one.덮음 ? { ...one.문서, 기계덮음: one.덮음 } : one.문서, links)); +for (const one of input.짜임 || []) { + const base = cover.defaultsOf(one.문서, one.값); + const key = cover.machineKey(one.입력); + out.짜임.push({ + 얹음: cover.coverDoc(one.문서, one.입력, one.덮음), + 기본: base, + 정리: one.덮음[key] ? cover.prune(one.덮음[key], base) : null, + }); +} +process.stdout.write(JSON.stringify(out)); diff --git a/resources/tester/test_836_1_machine_cover.py b/resources/tester/test_836_1_machine_cover.py new file mode 100644 index 000000000..55941ff82 --- /dev/null +++ b/resources/tester/test_836_1_machine_cover.py @@ -0,0 +1,336 @@ +"""836-1 기계 덮음 — 셈 규칙 · 기본값 · 정리 · 몸 검사 · 거울(파이썬 ↔ TS) 지킴 시험. + +짝 = `resources/master_data/scripts/master_doc_cover.py` ↔ `common_util/common_util_formula_doc_cover.ts` +(계산기 부름 자리 = `master_doc_calc` ↔ `common_util_formula_doc_calc.ts`). +① 덮음 없음 = 문서 그대로(같은 물건 · 같은 금액) · 남의 기계 덮음 = 안 얹음. +② 굴착기(무한궤도) 1.0㎥(0201-0100 · GC000267) — 값 · 조종원 · 잡품 덮음 = 손셈(M01 값). +③ 조종원 없는 식에 조종원 더함(시간환산 표 더함) · 잡품 없는 식에 잡품 더함 · 연료 + 전력 식 기본 잡품 둘. +④ 정리 — 원본과 같으면 지움 · 안 부르는 기계 지움 · 몸 검사 틀림. +⑤ 거울 — 같은 연결 값으로 파이썬 · TS 답 · 얹은 문서 · 기본값 · 정리가 같음. +""" + +import importlib +import json +import os +import subprocess +from decimal import Decimal as D + +import pytest + +from M01_MasterData import M01_MasterData_Store as m01 # scripts 길을 둠 + +mc = importlib.import_module("master_doc_calc") +ml = importlib.import_module("master_doc_link") +mcv = importlib.import_module("master_doc_cover") + +HERE = os.path.dirname(os.path.abspath(__file__)) +COSTS = ("노무비", "재료비", "경비", "계") +NO = "0201-0100" # 굴착기(무한궤도) 1.0㎥ — 조사 846 1 장 견본 + + +@pytest.fixture(scope="module") +def links(): + _, whole = m01.loaded() + return ml.Links(whole) + + +def _one(logic: str, no: str, cover: dict | None = None) -> dict: + """기계 한 줄 문서(1 hr) — 덮음 = 문서 칸.""" + var = {"열쇠": "P_기계", "기호": "P", "아래": "기계", "위": "", "이름": "기계", "단위": "", + "연결": {"갈래": "단가산출", "키": logic, "입력": {"기계": f"'{no}'"}}} # fmt: skip + line = {"이름": "기계", "단위": "hr", "비목": "묶음", "수량": "1", "단가": "P_기계"} + doc = {"꼴": "수식문서", "판": 1, "키": "_t", "이름": "t", "결과단위": "원", + "변수": [var], "식": [], "금액": [line]} # fmt: skip + return {**doc, mcv.COVER: cover} if cover else doc + + +def _money(ans): + return {k: ans.get(k) for k in COSTS} + + +def _first(links, logic: str) -> str: + var = next(v for v in links.doc(logic)["변수"] if v.get("열쇠") == "기계") + return str(var["연결"]["목록"][0]) + + +def _orig(links, logic: str, no: str) -> dict: + memo: dict = {} + mc.calc(_one(logic, no), links, None, (), memo) + return next(a for (k, _), a in memo.items() if k == logic) + + +# ── ① 덮음 없음 ──────────────────────────────────────────────────────── +def test_덮음_없음_같은_물건_같은_금액(links): + doc = links.doc("GC000267") + assert mcv.cover_doc(doc, {"기계": NO}, {}) is doc + assert mcv.cover_doc(doc, {"기계": NO}, {"0201-0120": {"값": {"가격": 1}}}) is doc + assert mcv.cover_doc(doc, {}, {NO: {"값": {"가격": 1}}}) is doc + base = mc.calc(_one("GC000267", NO), links) + other = mc.calc(_one("GC000267", NO, {"0201-0120": {"값": {"가격": 1}}}), links) + assert _money(base) == _money(other) + assert base["계"] == D(108958) or base["셈됨"] # M01 값이 바뀌면 금액은 달라도 됨(값 대조는 ②) + + +# ── ② 손셈 ──────────────────────────────────────────────────────────── +def _cut1(x): + return D(str(x)).quantize(D("0.1"), rounding="ROUND_DOWN") + + +def test_굴착기_덮음_손셈(links): + v = _orig(links, "GC000267", NO)["값"] + price, fuel, oil = v["P_기계"], v["P_기계주연료"], v["P_자동차용경유"] + wage, rate = v["w_기계운전사"], v["r_기계잡재료"] + k = (v["k_기계상각비"] + v["k_기계정비비"]) * (1 + v["r_가산"] / 100) + v["k_기계관리비"] + + def loss(p): + return (D(p) * 1000 * k / D(10) ** 7).to_integral_value(rounding="ROUND_DOWN") + + def total(labor, mats, exp): + return {"노무비": int(labor), "재료비": int(mats), "경비": int(exp)} + + fuel_amt = _cut1(fuel * oil) + crew1 = _cut1(D(1) / 8 * wage) + misc = _cut1(fuel_amt * rate / 100) + want = total(crew1, fuel_amt + misc, loss(price)) + got = mc.calc(_one("GC000267", NO), links) + assert {c: int(got[c]) for c in ("노무비", "재료비", "경비")} == want + + def run(cover): + a = mc.calc(_one("GC000267", NO, {NO: cover}), links) + assert a["셈됨"], a["표지"] + return {c: int(a[c]) for c in ("노무비", "재료비", "경비")} + + assert run({"값": {"가격": 150000}}) == total(crew1, fuel_amt + misc, loss(150000)) + f20 = _cut1(20 * oil) + assert run({"값": {"주연료": 20}}) == total(crew1, f20 + _cut1(f20 * rate / 100), loss(price)) + assert run({"조종원": [{"키": "LB:1048", "인": 2}]}) == total( + _cut1(D(2) / 8 * wage), fuel_amt + misc, loss(price) + ) + assert run({"조종원": []}) == total(0, fuel_amt + misc, loss(price)) + assert run({"잡품": [{"줄": "주연료", "율": 30}]}) == total( + crew1, fuel_amt + _cut1(fuel * oil * 30 / 100), loss(price) + ) + assert run({"잡품": []}) == total(crew1, fuel_amt, loss(price)) + # 다른 직종 · 사람 둘 = 두 줄 + two = run({"조종원": [{"키": "LB:1048", "인": 1}, {"키": "LB:1002", "인": 1}]}) + labor = mc.calc(_one("GC000267", NO, {NO: {"조종원": [{"키": "LB:1002", "인": 1}]}}), links) + assert two["노무비"] == int(crew1) + int(labor["노무비"]) + + +# ── ③ 줄 더함 ───────────────────────────────────────────────────────── +def test_조종원_없는_식에_조종원_잡품_없는_식에_잡품(links): + no = _first(links, "GC000272") + base = mc.calc(_one("GC000272", no), links) + got = mc.calc(_one("GC000272", no, {no: {"조종원": [{"키": "LB:1048", "인": 1}]}}), links) + assert got["셈됨"], got["표지"] + assert got["노무비"] > 0 and got["재료비"] == base["재료비"] and got["경비"] == base["경비"] + no3 = _first(links, "GC000273") + base3 = mc.calc(_one("GC000273", no3), links) + got3 = mc.calc( + _one("GC000273", no3, {no3: {"잡품": [{"키": "LB:1048", "수량": 1, "율": 10}]}}), links + ) + assert got3["셈됨"] and got3["재료비"] > base3["재료비"] == 0 + + +def test_연료_전력_식_기본_잡품_둘(links): + doc = links.doc("GC001200") + parts = mcv.parts_of(doc) + assert parts["misc"] is not None and len(parts["base"]) == 2 + + +# ── ④ 정리 · 검사 ───────────────────────────────────────────────────── +def test_원본과_같으면_지움_안_부르는_기계_지움(links): + v = _orig(links, "GC000267", NO)["값"] + same = {NO: {"값": {"가격": v["P_기계"]}, "조종원": [{"키": "LB:1048", "인": 1}], + "잡품": [{"줄": "주연료", "율": v["r_기계잡재료"]}]}} # fmt: skip + assert mcv.COVER not in mcv.prune_doc(_one("GC000267", NO, same), links) + mixed = {NO: {"값": {"가격": v["P_기계"], "주연료": 20}}, "9999-9999": {"값": {"가격": 1}}} + got = mcv.prune_doc(_one("GC000267", NO, mixed), links)[mcv.COVER] + assert got == {NO: {"값": {"주연료": 20}}} + + +@pytest.mark.parametrize( + "bad", + [ + [], + {NO: 1}, + {NO: {"값": {"가격": -1}}}, + {NO: {"값": {"조종원": 2}}}, + {NO: {"조종원": [{"키": "", "인": 1}]}}, + {NO: {"조종원": [{"키": "LB:1048", "인": -1}]}}, + {NO: {"잡품": [{"율": 10}]}}, + {NO: {"잡품": [{"줄": "주연료", "율": "x"}]}}, + ], +) +def test_몸_검사_틀림(bad): + with pytest.raises(ValueError): + mcv.clean(bad) + + +def test_몸_검사_빈_칸_뺌(): + assert mcv.clean({NO: {"값": {"가격": ""}}}) == {} + assert mcv.clean({NO: {"값": {"가격": 0}}}) == {NO: {"값": {"가격": 0}}} + + +# ── ⑤ 거울 ──────────────────────────────────────────────────────────── +def _canon(v): + if isinstance(v, (D, int, float)) and not isinstance(v, bool): + return mc.plain_text(D(str(v))) + if isinstance(v, list): + return [_canon(x) for x in v] + if isinstance(v, dict): + return {k: _canon(v[k]) for k in sorted(v)} + return v + + +class Rec: + def __init__(self, links): + self.links, self.log = links, {} + + def _keep(self, kind, p, fn): + key = f"{kind}|{json.dumps(_canon(p), ensure_ascii=False, separators=(',', ':'))}" + try: + got = fn(p) + except mc.Gap as e: + self.log[key] = {"빈": str(e)} + raise + except mc.Broken as e: + self.log[key] = {"끊김": str(e)} + raise + self.log[key] = {"값": got} + return got + + def element(self, p): + return self._keep("요소", p, self.links.element) + + def table(self, p): + return self._keep("표", p, self.links.table) + + def doc(self, key): + return self.links.doc(key) + + +def _same(a, b, where=""): + if isinstance(a, dict): + assert isinstance(b, dict) and set(a) == set(b), (where, sorted(a), sorted(b or {})) + for k in a: + _same(a[k], b[k], f"{where}.{k}") + elif isinstance(a, list): + assert isinstance(b, list) and len(a) == len(b), where + for i, (x, y) in enumerate(zip(a, b)): + _same(x, y, f"{where}[{i}]") + elif isinstance(a, (D, int, float)) and not isinstance(a, bool): + assert isinstance(b, (int, float)) and not isinstance(b, bool), (where, a, b) + assert float(a) == pytest.approx(b, rel=1e-9, abs=1e-9), (where, a, b) + else: + assert a == b, (where, a, b) + + +COVERS = [ + None, + {"값": {"가격": 150000, "주연료종류": "휘발유"}}, + {"조종원": [{"키": "LB:1048", "인": 2}, {"키": "LB:1002", "인": 0.5}]}, + {"조종원": []}, + {"잡품": [{"줄": "주연료", "율": 30}, {"키": "LB:1048", "이름": "시험", "수량": 2, "율": 5}]}, + {"잡품": [{"줄": "주연료", "수량": 10, "율": 22}]}, +] + + +def test_거울_파이썬_TS_같은_답(links): + rec = Rec(links) + picks = [("GC000267", NO)] + [ + (k, _first(links, k)) + for k in ("GC000272", "GC000273", "GC000275", "GC000276", "GC001196", "GC001197") + ] + cases, want, shapes, shape_want = [], [], [], [] + for logic, no in picks: + orig = _orig(Rec(links), logic, no) # 기록에 안 섞임(원본 셈은 아래에서 다시) + for one in COVERS: + cover = {no: one} if one else None + doc = _one(logic, no) + cases.append({"문서": doc, "덮음": cover}) + want.append(mc.calc({**doc, mcv.COVER: cover} if cover else doc, rec)) + if cover: + base = mcv.defaults_of(links.doc(logic), orig["값"]) + shapes.append({"문서": links.doc(logic), "입력": {"기계": no}, "덮음": cover, + "값": orig["값"]}) # fmt: skip + shape_want.append({"얹음": mcv.cover_doc(links.doc(logic), {"기계": no}, cover), + "기본": base, "정리": mcv.prune(cover[no], base)}) # fmt: skip + docs = {k: links.doc(k) for k, _ in picks} + payload = {"문서들": docs, "대상": cases, "기록": rec.log, "짜임": shapes} + proc = subprocess.run( + ["node", os.path.join(HERE, "helper_836_1_machine_cover.cjs")], + input=json.dumps(ml._norm(payload), ensure_ascii=False), + capture_output=True, + text=True, + encoding="utf-8", + check=True, + ) + got = json.loads(proc.stdout) + for i, (a, b) in enumerate(zip(want, got["답"])): + _same(ml._norm(_money(a)), _money(b), f"답{i}") + _same(ml._norm(a["값"]), b["값"], f"값{i}") + assert len(got["짜임"]) == len(shape_want) + for i, (a, b) in enumerate(zip(shape_want, got["짜임"])): + _same(ml._norm(a), b, f"짜임{i}") + + +# ── ⑥ 서버 저장 길(임시 층 · 수식 길) ──────────────────────────────────── +@pytest.fixture +def world(tmp_path, monkeypatch): + from resources.tester.test_m02_labor_ref import world as make + + return make.__wrapped__(tmp_path, monkeypatch) + + +def test_저장_다시_열기_되돌리면_덮음_없음(world): + import copy as cp + + from M02_MasterTemplete import M02_Doc_Sk_Path as path + from M02_MasterTemplete import M02_Labor_Ref as refs + from M02_MasterTemplete import M02_MasterTemplete_Store as store + + me = world["personal"] + + def at(fn, *args): + with store.at(me): + return fn(*args) + + key = at( + lambda: refs.create("sangeun", {"이름": "sub3임시 덮음", "단위": "시간", "더한줄": []}) + ) + key = key["이름"] + got = at(path.read, key) + doc = cp.deepcopy(got["문서"]) + link = {"갈래": "단가산출", "키": "GC000267", "입력": {"기계": f"'{NO}'"}} + for s in ("기계", "기계둘"): + doc.setdefault("변수", []).append( + {"기호": "P", "아래": s, "위": "", "이름": s, "단위": "", "연결": link} + ) + doc.setdefault("금액", []).append( + {"이름": s, "단위": "hr", "비목": "묶음", "수량": "1", "단가": f"P_{s}"} + ) + at(path.save, key, {"문서": doc, "판": got["판"]}) + base = at(path.read, key) + was = base["셈"]["계"] + one = base["셈"]["줄"][0]["금액"] + doc = cp.deepcopy(base["문서"]) + doc[mcv.COVER] = {NO: {"값": {"가격": 150000}}, "9999-9999": {"값": {"가격": 1}}} + after = at(path.save, key, {"문서": doc, "판": base["판"]}) + assert after["문서"][mcv.COVER] == {NO: {"값": {"가격": 150000}}} # 안 부르는 기계 = 뺌 + lines = after["셈"]["줄"] + assert lines[0]["금액"] == lines[1]["금액"] > one # 같은 기계 두 줄 = 같이 바뀜 + again = at(path.read, key) + assert again["문서"][mcv.COVER] == {NO: {"값": {"가격": 150000}}} + assert again["셈"]["계"] == after["셈"]["계"] > was + v = _orig(ml.Links(m01.loaded()[1]), "GC000267", NO)["값"] + doc = cp.deepcopy(again["문서"]) + doc[mcv.COVER] = {NO: {"값": {"가격": v["P_기계"]}}} # 되돌림 = 원본 값 + back = at(path.save, key, {"문서": doc, "판": again["판"]}) + assert mcv.COVER not in back["문서"] and back["셈"]["계"] == was + with store.at(me): + file = store.read("sangeun", key)["문서"] + assert mcv.COVER not in file["수식"]["문서"] + doc[mcv.COVER] = {NO: {"값": {"가격": -5}}} + with pytest.raises(store.StoreError): + at(path.save, key, {"문서": doc, "판": back["판"]}) From 2ee8cfbfbed2e82215c24eeb003cfcc78dfaed75 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sun, 15 Nov 2026 13:31:18 +0900 Subject: [PATCH 06/11] =?UTF-8?q?chore(ledger):=20=EC=9A=94=EA=B5=AC=20859?= =?UTF-8?q?=20=EC=9E=A5=EB=B6=80=20=E2=80=94=20=ED=91=9C=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=EC=A7=80=20=EC=B9=A9=20=EC=A4=91=EB=B3=B5=20=C2=B7=20?= =?UTF-8?q?=EA=B8=B0=EA=B3=84=20=EA=B3=A0=EB=A5=B4=EA=B8=B0=20=EB=B3=80?= =?UTF-8?q?=EC=88=98=20=ED=92=88=EB=B2=88=20=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude --- docs/raw/요구_1104/사용자요구_1104.md | 6 ++++++ .../요구_1104/요구859_굴착기_품번_값없음.png | Bin 0 -> 83813 bytes .../요구859_표값_인력줄_표선택지_중복.png | Bin 0 -> 37797 bytes 3 files changed, 6 insertions(+) create mode 100644 docs/raw/요구_1104/요구859_굴착기_품번_값없음.png create mode 100644 docs/raw/요구_1104/요구859_표값_인력줄_표선택지_중복.png diff --git a/docs/raw/요구_1104/사용자요구_1104.md b/docs/raw/요구_1104/사용자요구_1104.md index c704c3604..7eaf0695b 100644 --- a/docs/raw/요구_1104/사용자요구_1104.md +++ b/docs/raw/요구_1104/사용자요구_1104.md @@ -1372,3 +1372,9 @@ est원본에서 잘린 글씨는 보이는 만큼만 서울/경기로 표현하 기호 이름을 사용자가 바꾸면 어쩔수 없지만 벌써 품셈에서 정의된 기호라는거지. 좌우측 영역에서 바꿀 필요는 없다는 말이야. [Image #1] 여기를 보면 P기계, k 기계상각비 라고 표현되는데 경쟁사에서 어떻게 표현되어 있는지 확인해봐. 깔끔하잖아. GC000089의 단가품셈 기초의 항목을 내가 하나 삭제했음. 되돌림 했는데 되돌림이 안됨. 확인 후 개선하고 데이터도 원복해줘. + +## 요구 859 (11-15 · 사용자 · 추가 업무 — ① 수식 보기 왼쪽 「표 값」 항목과 아래 인력 줄에 표 선택지가 중복으로 보임 · 없어도 됨(그림 요구859_표값_인력줄_표선택지_중복.png) ② 굴착기 항목이 왼쪽에 품번(0201-0100)으로 나오고 오른쪽 산출근거에 계산 값이 없음(—) · 괜찮은지 확인(그림 요구859_굴착기_품번_값없음.png)) + +추가 업무 +[Image #1]1. 여기에서 표 값에 항목과 아래 인력 항목에 중복의로 표 선택지를 보여주고 있음. 내용은 업어도 될듯. +2. [Image #2] 여기에서 좌측 굴착기 항목이 품번으로 나오고 우측에 계산 값이 없는데 괜찮은 건지 확인 diff --git a/docs/raw/요구_1104/요구859_굴착기_품번_값없음.png b/docs/raw/요구_1104/요구859_굴착기_품번_값없음.png new file mode 100644 index 0000000000000000000000000000000000000000..436f592fd7b90740a820b2cea610f6c778ebe595 GIT binary patch literal 83813 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

CEHyE5p?R38 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≷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 Date: Sun, 15 Nov 2026 13:35:01 +0900 Subject: [PATCH 07/11] =?UTF-8?q?feat(836-2):=20M02=20=EB=8B=A8=EA=B0=80?= =?UTF-8?q?=EC=82=B0=EC=B6=9C=20M01=20=EC=9E=90=EB=8F=99=20=EB=94=B0?= =?UTF-8?q?=EB=9D=BC=EC=98=B4=20=E2=80=94=20=EC=84=9C=EB=B2=84(=EC=82=AC?= =?UTF-8?q?=EB=B3=B8=20=EC=A4=84=20=C2=B7=20=EB=B6=80=EB=A6=84=20=EB=AC=B8?= =?UTF-8?q?=EC=84=9C=20=C2=B7=20=EA=B3=A0=EC=B9=9C=20=ED=95=AD=EB=AA=A9=20?= =?UTF-8?q?=EB=8B=A4=EC=8B=9C=20=EB=B3=B5=EC=82=AC=20=C2=B7=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 새 M02_Sk_AutoRenew — 프로젝트 층 밖 SK 사본 줄 = 지금 M01(+ 내 기계) · 부름 문서 = 지금 M01 수식 문서 - 고침 판정 = 내용 대조(끊음 대 부름 · 따로 문서 대 처음) · 바꿨다 되돌림 = 안 고침 - M01 에서 바뀐 항목의 고침 = 지움 + 다시 복사 + 열기 답 사본.갱신 알림 · 사본.자동 표지 - 파일은 [저장] · 알림 확인(저장) 때만 씀 · 읽기 길 안 씀 · 프로젝트 층 그대로 - 옛 시험 8 개 = 손 갱신 길(프로젝트 층 꼴 · 자동 끔) 고정 — 개인 층 「금액 그대로」 단언이 낡음 - 새 시험 test_836_2_sk_auto_renew(㉠ ~ ㉦) Co-Authored-By: Claude Opus 5.5 --- M02_MasterTemplete/M02_Doc_Sk.py | 6 + M02_MasterTemplete/M02_Doc_Sk_Path.py | 9 +- M02_MasterTemplete/M02_Sangeun_Copy.py | 14 +- M02_MasterTemplete/M02_Sk_AutoRenew.py | 190 ++++++++++++++++ resources/tester/test_795_1_sk_copy.py | 9 + resources/tester/test_796_2b_m02_equation.py | 9 + resources/tester/test_802_4a_m02_doc.py | 9 + resources/tester/test_821_1_my_machine.py | 9 + resources/tester/test_822_1_copy_gap.py | 9 + resources/tester/test_823_1_imported_swap.py | 11 + resources/tester/test_826_3_copy_slim.py | 11 + resources/tester/test_827_1_machine_window.py | 9 + resources/tester/test_836_2_sk_auto_renew.py | 213 ++++++++++++++++++ 13 files changed, 504 insertions(+), 4 deletions(-) create mode 100644 M02_MasterTemplete/M02_Sk_AutoRenew.py create mode 100644 resources/tester/test_836_2_sk_auto_renew.py diff --git a/M02_MasterTemplete/M02_Doc_Sk.py b/M02_MasterTemplete/M02_Doc_Sk.py index beaf9e4df..0d532ecbc 100644 --- a/M02_MasterTemplete/M02_Doc_Sk.py +++ b/M02_MasterTemplete/M02_Doc_Sk.py @@ -27,6 +27,7 @@ from M02_MasterTemplete import M02_Doc_Import as dim from M02_MasterTemplete import M02_MyMachine_Draft as draft from M02_MasterTemplete import M02_Sangeun_Copy as skc from M02_MasterTemplete import M02_Sangeun_Doc as sd +from M02_MasterTemplete import M02_Sk_AutoRenew as renew KEY = dc.KEY OLD_FIELDS = ( @@ -119,6 +120,7 @@ def copy_of(sk: dict, ctx: tuple | None) -> dict | None: got = sk.get(KEY) if dc.has(sk) else seed(sk) if got is None: return None + got = renew.form(sk, got) # M01 자동 따라옴(836-2) if got.get("따로") or ctx is None: return got if dim.is_imported(sk): # 가져온 식 — 엑셀 단가 기준 줄에서 지음(우리 = 한 번 더 돎) @@ -133,6 +135,7 @@ def copy_of(sk: dict, ctx: tuple | None) -> dict | None: except dc._ERR as e: root = {**dc.md.blank(str(sk.get("키"))), "_틀림": str(e)} values = dc.values_of(sk[skc.KEY]) if skc.has(sk) else dc.values_of(got) # 서버가 정한 값 + values = renew.rows(values) if renew.is_sk(sk) else values # 836-2 whole = dc.over(values) calls = dc.closure(whole, dc.calls(root), got.get("부름")) base = str((sk.get("바탕") or {}).get("로직") or "") @@ -191,6 +194,7 @@ def settle(out: dict, whole: Any, job: Any, raw: Any) -> dict: return {**out, KEY: norm(mine["사본"])} file = stored(out.get("키")) or {} have = file.get(KEY) if dc.has(file) else (raw.get(KEY) if dc.has(raw) else None) + have = renew.form(out, have) if have else have # 836-2 — 바뀐 항목 고침 지움 · 저장 때 굳음 if have and have.get("따로") and job != skc.REGEN: if file and old_fp(file) != old_fp(out): raise sd.DocError( @@ -210,6 +214,8 @@ def settle(out: dict, whole: Any, job: Any, raw: Any) -> dict: calc.run(out, whole, catch=box) calls = {} if job == skc.REGEN else (have or {}).get("부름") or {} first = {**(dc.values_of(out[skc.KEY]) if skc.has(out) else {}), "부름": calls} + if job != skc.REGEN and (have or {}).get("끊음"): # 안 바뀐 항목 고침 = 그대로(836-2) + first["끊음"] = have["끊음"] copy = copy_of({**out, KEY: first}, box[-1] if box else None) if ( copy and skc.has(out) and skc.READS.get() is None and (keys := gap.gaps(copy)) diff --git a/M02_MasterTemplete/M02_Doc_Sk_Path.py b/M02_MasterTemplete/M02_Doc_Sk_Path.py index e740a5101..b1408ca5b 100644 --- a/M02_MasterTemplete/M02_Doc_Sk_Path.py +++ b/M02_MasterTemplete/M02_Doc_Sk_Path.py @@ -25,6 +25,7 @@ from M02_MasterTemplete import M02_MasterTemplete_Store as store from M02_MasterTemplete import M02_MyMachine_Draft as draft from M02_MasterTemplete import M02_Sangeun_Copy as skc from M02_MasterTemplete import M02_Sangeun_Doc as sd +from M02_MasterTemplete import M02_Sk_AutoRenew as renew MAX = 2_000_000 # 문서 글 크기(바이트) 상한 JOBS = (skc.PRICE, skc.REGEN) @@ -77,7 +78,7 @@ def answer(copy: dict, sk: dict) -> dict[str, Any]: """{문서, 부름, 끊음, 셈, 사본 표지, 연결 기록} — 글 · 수는 JSON 꼴 · 가져온 식 표지 = 단가기준.""" got = dsk.run(sk, copy) head = {k: copy.get(k) for k in ("시점", "단가시점")} - mark = {**head, "따로": bool(copy.get("따로"))} + mark = {**head, "따로": bool(copy.get("따로")), "자동": renew.on()} # 836-2 M01 자동 따라옴 mark |= ( {"단가기준": dim.basis_of(sk)} if dim.is_imported(sk) @@ -97,7 +98,9 @@ def answer(copy: dict, sk: dict) -> dict[str, Any]: def read(key: str) -> dict[str, Any]: """읽기 — {문서, 부름, 끊음, 셈, 사본, 판}.""" got = _file(key) - return {**answer(current(got["문서"]), got["문서"]), "판": got["판"]} + out = {**answer(current(got["문서"]), got["문서"]), "판": got["판"]} + out["사본"]["갱신"] = renew.notes(got["문서"]) # 836-2 열 때 알림(지울 고침) + return out # ── 몸 검사 ─────────────────────────────────────────────────────────── @@ -137,6 +140,8 @@ def edited(copy: dict, body: dict, key: str, sk: dict | None = None) -> dict: out["부름"] = dc.closure(dc.over(out), calls, copy.get("부름")) out = gap.fill(out) # 셈이 읽은 줄 가운데 사본 없는 것 = 지금 값으로 박음(822-1) out = draft.renew(out) # 기계 창 초안으로 만든 · 고친 내 기계 = 새 값(827-1) + if not copy.get("따로"): # 고침 판정 원본(836-2) = 따로가 되기 전 문서 + out[renew.FIRST] = copy["문서"] out["따로"] = True return out diff --git a/M02_MasterTemplete/M02_Sangeun_Copy.py b/M02_MasterTemplete/M02_Sangeun_Copy.py index 65e3038c6..590cd571a 100644 --- a/M02_MasterTemplete/M02_Sangeun_Copy.py +++ b/M02_MasterTemplete/M02_Sangeun_Copy.py @@ -125,11 +125,18 @@ def _forget(whole: Any) -> None: _LIVE[0] = whole +def _auto(doc: Any, copy: dict) -> dict: + """SK 사본 = 프로젝트 층 밖이면 지금 M01 줄(836-2 · `M02_Sk_AutoRenew`).""" + from M02_MasterTemplete import M02_Sk_AutoRenew as renew + + return renew.rows(copy) if renew.is_sk(doc) else copy + + def over(doc: Any, whole: Any) -> Any: """사본 있는 문서 = 사본 색인 · 없으면 `whole` 그대로(옛 길).""" if not has(doc): return whole - copy = doc[KEY] + copy = _auto(doc, doc[KEY]) tag = (copy.get("판"), copy.get("단가판")) if isinstance(whole, Copy) and whole._tag == tag: return whole @@ -290,6 +297,8 @@ def settle(doc: Any, save: bool = False) -> Any: copy = refresh(copy, whole_())[0] if copy and any(isinstance(x, dict) and x.get("다시잇기") for x in out.get("더한줄") or []): copy = rejoin(copy, out["더한줄"]) + if copy: + copy = _auto(doc, copy) # M01 자동 따라옴(836-2) — 저장 때 굳음 if save and copy and READS.get() is None: # 읽은 줄만(826-3) = 저장 관문이 더함 copy = grow(copy, stamp(out)) out = _formula_job(out, job) @@ -305,7 +314,8 @@ def _formula_job(out: dict, job: Any) -> dict: if got is None or job not in (PRICE, REGEN): return out if job == REGEN: - got = {k: v for k, v in got.items() if k not in ("문서", "끊음", "따로")} | {"부름": {}} + drop = ("문서", "끊음", "따로", "처음") + got = {k: v for k, v in got.items() if k not in drop} | {"부름": {}} elif got.get("따로"): got = dc.refresh(got)[0] return {**out, dc.KEY: got} diff --git a/M02_MasterTemplete/M02_Sk_AutoRenew.py b/M02_MasterTemplete/M02_Sk_AutoRenew.py new file mode 100644 index 000000000..3aed98f79 --- /dev/null +++ b/M02_MasterTemplete/M02_Sk_AutoRenew.py @@ -0,0 +1,190 @@ +"""M02 단가산출(SK) M01 자동 따라옴(PLAN 836-2 · 요구 854) — 서버 단독. + +프로젝트 층 밖 SK(마스터 · 회사 · 개인) = 셈 · 열기 · 목록 · 일위대가 펼침이 모두 + 사본 줄 = 지금 M01(+ 층 내 기계) 줄(`rows` · 지운 키 = 사본 마지막 줄) · + 부름 문서 = 지금 M01 수식 문서(`form`)로 봄 — 묻지 않음 · [단가 갱신] · [재생성] 없음. +파일(작업본)은 [저장] · 알림 [확인](= 저장) 때만 새 것으로 씀 — 읽기 길은 안 씀 · + 저장 파일의 사본 = 마지막 저장 때 받은 M01(= 고침 판정의 「복사 당시 원본」). +고침 판정 = 내용 대조(표지 아님 · 바꿨다 되돌림 = 안 고침) — 셈에 닿는 칸(`LOGIC`)만 견줌: + 끊음[키] 대 부름[키](복사 당시) · 따로 문서 대 `처음`(따로가 된 때의 문서 · 없으면 고친 것). +M01 에서 바뀐 항목에 고침이 얹혀 있으면 그 고침을 지우고 다시 복사 + 알림 `갱신` 한 줄 + {키, 이름, 갈래, M01 [전 → 후], 내고침 [지워진 값]} · 고침 없는 항목 = 조용히 따라옴 · + 안 바뀐 항목의 고침 = 그대로. 따로 문서를 지우면 파생(옛 칸 + 새 M01)으로 돌아감. +가져온 식 SK = 값 줄만 따라옴(가져온 줄 문서 · 식 안 건드림 · 단가기준 「엑셀」 은 엑셀 값 그대로). +프로젝트 층 · B09 사본 = 그대로(사용자가 고르는 [단가 갱신] · [재생성]). +""" + +from __future__ import annotations + +import copy as cp +import json +from typing import Any + +from M02_MasterTemplete import M02_Doc_Copy as dc +from M02_MasterTemplete import M02_Item_Prices as prices +from M02_MasterTemplete import M02_Sangeun_Copy as skc +from M02_MasterTemplete import M02_Sangeun_Doc as sd +from M02_MasterTemplete import M02_Sangeun_Imported as imp + +FIRST = "처음" # `수식` 칸 — 따로가 된 때의 문서(고침 판정 원본) +LOGIC = ( + "변수", + "식", + "금액", + "끝수", + "결과단위", +) # 셈에 닿는 칸 — 쌓기(차림) · 이름 · 판은 안 견줌 +MAX_LINES = 8 +_MEMO: dict[tuple, dict] = {} +_LIVE: list[Any] = [None] + + +def on() -> bool: + """자동 따라옴 — 프로젝트 층 밖.""" + from M02_MasterTemplete import M02_Labor_Ref as refs + + return refs.here()[0] != "project" + + +def is_sk(doc: Any) -> bool: + return isinstance(doc, dict) and doc.get("종류") == sd.TITLE + + +# ── 값 줄 ────────────────────────────────────────────────────────────── +def _fresh(row: dict, live: Any) -> dict: + key = str(row.get("키")) + got = live.index.get(key[:2], {}).get(key) + if got is None: + return row # M01 에서 지운 키 = 마지막 값 + new = prices.to_json(got) + return row if skc._fp(new) == skc._fp(row) else new + + +def rows(copy: Any) -> Any: + """값 사본(`skc` 꼴) → 줄마다 지금 M01(+ 층 내 기계) · 바뀐 몫만 판 다시 · 프로젝트 층 = 그대로.""" + if not (isinstance(copy, dict) and isinstance(copy.get("로직"), list)) or not on(): + return copy + live = skc.whole_() + if live is not _LIVE[0]: + _MEMO.clear() + _LIVE[0] = live + tag = (copy.get("판"), copy.get("단가판"), len(copy["로직"]), len(copy.get("단가") or [])) + hit = _MEMO.get(tag) + if hit is not None: + return {**copy, **hit} + got: dict = {} + for part, mark in (("로직", "판"), ("단가", "단가판")): + old = copy.get(part) or [] + new = [_fresh(r, live) if isinstance(r, dict) else r for r in old] + if any(a is not b for a, b in zip(old, new)): + got |= {part: new, mark: skc._fp(new)} + if len(_MEMO) > 64: # ponytail: 통째 비움 · SK 가 많아 느려지면 LRU 로 + _MEMO.clear() + _MEMO[tag] = got + return {**copy, **got} + + +# ── 수식 사본(부름 · 끊음 · 따로 문서) ────────────────────────────────── +def _logic(doc: Any) -> str: + from M02_MasterTemplete import M02_Doc_Sk as dsk + + doc = doc if isinstance(doc, dict) else {} + return dc.fp(dsk._strip({k: doc.get(k) for k in LOGIC})) + + +def _say(v: dict) -> str: + link = v.get("연결") or {} + for slot in ("값", "식", "키"): + if link.get(slot) not in (None, ""): + return str(link[slot]) + return json.dumps(link, ensure_ascii=False, default=str)[:60] + + +def _name(v: dict) -> str: + return str(v.get("이름") or f"{v.get('기호') or ''}{v.get('아래') or ''}") + + +def diff(a: Any, b: Any) -> list[str]: + """두 문서의 셈 칸 다른 곳 — 「이름 전 → 후」(변수 · 금액 줄) · 많으면 앞 몇 줄.""" + a, b = (x if isinstance(x, dict) else {} for x in (a, b)) + out: list[str] = [] + va = {dc.md.var_id(v): v for v in a.get("변수") or []} + vb = {dc.md.var_id(v): v for v in b.get("변수") or []} + for vid in [*va, *(k for k in vb if k not in va)]: + x, y = va.get(vid), vb.get(vid) + if x and y and _say(x) == _say(y): + continue + name = _name(y or x) + out.append(f"{name} {_say(x) if x else '—'} → {_say(y) if y else '—'}") + la, lb = a.get("금액") or [], b.get("금액") or [] + for n in range(max(len(la), len(lb))): + x, y = (la[n] if n < len(la) else None), (lb[n] if n < len(lb) else None) + 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 + out.append(f"{name} {show(x)} → {show(y)}") + for slot in ("식", "끝수", "결과단위"): + if dc.fp(a.get(slot)) != dc.fp(b.get(slot)): + out.append(slot) + return out[:MAX_LINES] + ([f"… {len(out) - MAX_LINES}"] if len(out) > MAX_LINES else []) + + +def form(sk: Any, got: Any, notes: list | None = None) -> Any: + """수식 사본 → 지금 M01 으로 본 것(값 줄 · 부름) · M01 에서 바뀐 항목의 고침(끊음 · 따로 문서)은 + 지우고 `notes` 에 한 줄 · 안 바뀐 항목 고침 · 프로젝트 층 = 그대로.""" + if not isinstance(got, dict) or not on() or not is_sk(sk): + return got + out = rows(got) if "로직" in got else dict(got) + if imp.is_imported(sk): + return out # 가져온 식 = 값만(식 · 가져온 줄 그대로) + live = dc.folder() + calls = dict(out.get("부름") or {}) + changed = {k for k, d in calls.items() if k in live and dc.fp(d) != dc.fp(live[k])} + if not changed and not out.get("따로"): + return out + for k in changed: + calls[k] = cp.deepcopy(live[k]) + cut = dict(out.get("끊음") or {}) + for k in sorted(changed & set(cut)): + mine, was = cut.pop(k), (got.get("부름") or {}).get(k) + if _logic(mine) != _logic(was) and notes is not None: + notes.append(_note(k, was, live[k], diff(was, mine), "부름")) + out |= {"부름": calls, "끊음": cut} + base = out.get("원본") or {} + key = str(base.get("키") or "") + if out.get("따로") and key in live and dc.fp(live[key]) != base.get("판"): + first = out.get(FIRST) + mine = out.get("문서") or {} + if first is None or _logic(mine) != _logic(first): + mark = ( + diff(first, mine) + if first is not None + else ["수식 문서 고침(옛 저장본 — 원본 없음)"] + ) + if notes is not None: + notes.append(_note(key, first, live[key], mark, "문서", mine.get("이름"))) + out = {k: v for k, v in out.items() if k not in ("따로", "문서", FIRST)} + if changed and "문서" in out: # 새 문서가 부르는 키 = 닫힌 묶음 + keys = [*dc.calls(out["문서"]), *(c for d in cut.values() for c in dc.calls(d))] + out["부름"] = dc.closure(dc.over(out), keys, calls) + return out + + +def _note(key: str, was: Any, now: dict, mine: list[str], kind: str, name: Any = None) -> dict: + return { + "키": key, + "이름": str(name or now.get("이름") or key), + "갈래": kind, + "M01": diff(was, now) or ["바뀜"], + "내고침": mine, + } + + +def notes(sk: Any) -> list[dict]: + """열 때 알림 — 이 SK 저장본에서 지울 고침(없으면 []) · 가져온 식 · 프로젝트 층 = [].""" + if not is_sk(sk) or not dc.has(sk) or imp.is_imported(sk): + return [] + out: list[dict] = [] + form(sk, sk[dc.KEY], out) + return json.loads(json.dumps(dc.ml._norm(out), ensure_ascii=False, default=str)) diff --git a/resources/tester/test_795_1_sk_copy.py b/resources/tester/test_795_1_sk_copy.py index f0148bf0a..a7724b31f 100644 --- a/resources/tester/test_795_1_sk_copy.py +++ b/resources/tester/test_795_1_sk_copy.py @@ -23,6 +23,15 @@ from M02_MasterTemplete import M02_Sangeun_Copy as skc from M02_MasterTemplete import M02_Sangeun_Imported as imp from resources.tester.test_m02_labor_ref import world # noqa: F401 — 같은 임시 층 + +@pytest.fixture(autouse=True) +def _manual(monkeypatch): + """836-2 — 이 시험 = 손 갱신 길(프로젝트 층 꼴 · 자동 따라옴 끔) · 자동 = test_836_2.""" + from M02_MasterTemplete import M02_Sk_AutoRenew as renew + + monkeypatch.setattr(renew, "on", lambda: False) + + PCT = "GC001109" # 원/㎡ · 인력 둘 + 「노무비 * 3 / 100」 MAN = "LB000002" # 보통인부 diff --git a/resources/tester/test_796_2b_m02_equation.py b/resources/tester/test_796_2b_m02_equation.py index 5bddc1dcf..6b0a8e65b 100644 --- a/resources/tester/test_796_2b_m02_equation.py +++ b/resources/tester/test_796_2b_m02_equation.py @@ -32,6 +32,15 @@ from resources.tester.test_795_1_sk_copy import ( ) from resources.tester.test_m02_labor_ref import world # noqa: F401 — 같은 임시 층 + +@pytest.fixture(autouse=True) +def _manual(monkeypatch): + """836-2 — 이 시험 = 손 갱신 길(프로젝트 층 꼴 · 자동 따라옴 끔) · 자동 = test_836_2.""" + from M02_MasterTemplete import M02_Sk_AutoRenew as renew + + monkeypatch.setattr(renew, "on", lambda: False) + + KEY = "GC000211" # 합판거푸집 — 중간 시공량 · 형틀목공 · 보통인부 · 호표 0 = 형틀목공 DOUBLE = [{"할일": "식", "자리": "호표:0", "식": "형틀목공 × 2", "까닭": "sub2임시 시험"}] diff --git a/resources/tester/test_802_4a_m02_doc.py b/resources/tester/test_802_4a_m02_doc.py index db29f9e22..87d4157f6 100644 --- a/resources/tester/test_802_4a_m02_doc.py +++ b/resources/tester/test_802_4a_m02_doc.py @@ -28,6 +28,15 @@ from resources.tester.test_795_2_sc_copy import LB, PCT, _double, _labor, _sc from resources.tester.test_795_2_sc_copy import _calc as _sc_calc from resources.tester.test_m02_labor_ref import world # noqa: F401 — 같은 임시 층 + +@pytest.fixture(autouse=True) +def _manual(monkeypatch): + """836-2 — 이 시험 = 손 갱신 길(프로젝트 층 꼴 · 자동 따라옴 끔) · 자동 = test_836_2.""" + from M02_MasterTemplete import M02_Sk_AutoRenew as renew + + monkeypatch.setattr(renew, "on", lambda: False) + + FORM = "GC000211" # 합판거푸집(옵션 많음) BASIC = "GF000089" # 토사깍기(기계) — 굴착기 시간당 × 1 / 작업량(813-2 GC001148 지움 뒤) ADD = {"종류": "로직", "로직": BASIC, "수량": 2, "입력값": {}, "이름": "sub2임시 굴착기"} diff --git a/resources/tester/test_821_1_my_machine.py b/resources/tester/test_821_1_my_machine.py index 108d6c920..fedef9179 100644 --- a/resources/tester/test_821_1_my_machine.py +++ b/resources/tester/test_821_1_my_machine.py @@ -26,6 +26,15 @@ from M02_MasterTemplete import M02_Sangeun_Copy as skc from M02_MasterTemplete import M02_Template_Layers as layers from resources.tester.test_m02_labor_ref import world # noqa: F401 — 같은 임시 층 + +@pytest.fixture(autouse=True) +def _manual(monkeypatch): + """836-2 — 이 시험 = 손 갱신 길(프로젝트 층 꼴 · 자동 따라옴 끔) · 자동 = test_836_2.""" + from M02_MasterTemplete import M02_Sk_AutoRenew as renew + + monkeypatch.setattr(renew, "on", lambda: False) + + DIG = "EQ000022" # 굴착기(무한궤도) 1.0㎥ — 연료 · 조종원 있음 PCT = "GC001109" # M01 만 쓰는 SK 바탕 NAME = "서브셋임시기계" # 찾기 기종 글(수 = 규격으로 읽음 · 창 표시는 글로) diff --git a/resources/tester/test_822_1_copy_gap.py b/resources/tester/test_822_1_copy_gap.py index 04d56e83f..a1b4fef11 100644 --- a/resources/tester/test_822_1_copy_gap.py +++ b/resources/tester/test_822_1_copy_gap.py @@ -22,6 +22,15 @@ from M02_MasterTemplete import M02_MasterTemplete_Store as store from M02_MasterTemplete import M02_Sangeun_Copy as skc from resources.tester.test_m02_labor_ref import world # noqa: F401 — 같은 임시 층 + +@pytest.fixture(autouse=True) +def _manual(monkeypatch): + """836-2 — 이 시험 = 손 갱신 길(프로젝트 층 꼴 · 자동 따라옴 끔) · 자동 = test_836_2.""" + from M02_MasterTemplete import M02_Sk_AutoRenew as renew + + monkeypatch.setattr(renew, "on", lambda: False) + + DIG, DIG_NO, DIG_LOGIC = "EQ000022", "0201-0100", "GC000267" # 굴착기(무한궤도) 1.0㎥ DIG2_NO = "0201-0070" # 바꿔 끼울 굴착기 MAN = "LB000002" # 보통인부 diff --git a/resources/tester/test_823_1_imported_swap.py b/resources/tester/test_823_1_imported_swap.py index 3704b9561..5d79d1d7d 100644 --- a/resources/tester/test_823_1_imported_swap.py +++ b/resources/tester/test_823_1_imported_swap.py @@ -14,6 +14,8 @@ import json import os import subprocess +import pytest + from M02_MasterTemplete import M02_Doc_Copy as dc from M02_MasterTemplete import M02_Doc_Import as dim from M02_MasterTemplete import M02_Doc_Sk as dsk @@ -24,6 +26,15 @@ from M02_MasterTemplete import M02_Sangeun_Imported as imp from resources.tester.test_822_1_copy_gap import _move from resources.tester.test_m02_labor_ref import world # noqa: F401 — 같은 임시 층 + +@pytest.fixture(autouse=True) +def _manual(monkeypatch): + """836-2 — 이 시험 = 손 갱신 길(프로젝트 층 꼴 · 자동 따라옴 끔) · 자동 = test_836_2.""" + from M02_MasterTemplete import M02_Sk_AutoRenew as renew + + monkeypatch.setattr(renew, "on", lambda: False) + + HERE = os.path.dirname(__file__) DIG = "GC000267" # 굴착기 시간당 경비 NOW, NEW, NEW_EQ = "'0201-0070'", "'0201-0100'", "EQ000022" # 0.7㎥ → 1.0㎥ diff --git a/resources/tester/test_826_3_copy_slim.py b/resources/tester/test_826_3_copy_slim.py index 0961ef5cc..47faba6b8 100644 --- a/resources/tester/test_826_3_copy_slim.py +++ b/resources/tester/test_826_3_copy_slim.py @@ -14,6 +14,8 @@ import copy import json from pathlib import Path +import pytest + from M01_MasterData import M01_MasterData_Store as m01 from M02_MasterTemplete import M02_Doc_Sk_Path as path from M02_MasterTemplete import M02_Item_Prices as prices @@ -36,6 +38,15 @@ from resources.tester.test_822_1_copy_gap import ( ) from resources.tester.test_m02_labor_ref import world # noqa: F401 — 같은 임시 층 + +@pytest.fixture(autouse=True) +def _manual(monkeypatch): + """836-2 — 이 시험 = 손 갱신 길(프로젝트 층 꼴 · 자동 따라옴 끔) · 자동 = test_836_2.""" + from M02_MasterTemplete import M02_Sk_AutoRenew as renew + + monkeypatch.setattr(renew, "on", lambda: False) + + DIG_BASE = "GC000088" # 터파기(기계) 보통토사 — 「기계」 고르기 후보를 닫힌 묶음이 다 담음 diff --git a/resources/tester/test_827_1_machine_window.py b/resources/tester/test_827_1_machine_window.py index 0c08055ed..a3259c05d 100644 --- a/resources/tester/test_827_1_machine_window.py +++ b/resources/tester/test_827_1_machine_window.py @@ -25,6 +25,15 @@ from M02_MasterTemplete import M02_MyMachine_Draft as draft from M02_MasterTemplete import M02_MyMachine_Router as route from resources.tester.test_m02_labor_ref import world # noqa: F401 — 같은 임시 층 + +@pytest.fixture(autouse=True) +def _manual(monkeypatch): + """836-2 — 이 시험 = 손 갱신 길(프로젝트 층 꼴 · 자동 따라옴 끔) · 자동 = test_836_2.""" + from M02_MasterTemplete import M02_Sk_AutoRenew as renew + + monkeypatch.setattr(renew, "on", lambda: False) + + DIG = "EQ000022" # 굴착기(무한궤도) 1.0㎥ — 연료 · 조종원 있음 NAME = "sub1임시 기계" USER = {"role": "USER", "company_id": 7, "user_id": 42} diff --git a/resources/tester/test_836_2_sk_auto_renew.py b/resources/tester/test_836_2_sk_auto_renew.py new file mode 100644 index 000000000..6b52cf995 --- /dev/null +++ b/resources/tester/test_836_2_sk_auto_renew.py @@ -0,0 +1,213 @@ +# ruff: noqa: F811 — 임시 층 fixture(world)를 test_m02_labor_ref 에서 받아 씀 +"""836-2 M01 → M02 단가산출 자동 따라옴(요구 854) — 임시 storage + M01 메모리 사본(정본 파일 안 고침). + +㉠ 노임 · 재료 · 기계 값 바뀜 = 누르지 않고 새 금액(수식 길 열기 · 옛 셈 길 목록 · 일위대가 펼침 같은 답) +㉡ 부른 M01 수식 문서 바뀜 = 따라옴 ㉢ 고친 문서 + 그 문서 M01 바뀜 = 다시 복사 + 알림(전 · 후 · 내 고침) +㉣ 고침 + 다른 항목만 바뀜 = 고침 그대로 ㉤ 바꿨다 되돌림 = 안 고침(알림 없음) +㉥ 프로젝트 층 = 그대로 ㉦ 가져온 식(우리) = 값만 · 식 그대로 · 열기 = 파일 안 고침 · 알림 [확인] = 저장. +""" + +from __future__ import annotations + +import copy +import shutil + +import pytest + +from M02_MasterTemplete import M02_Doc_Copy as dc +from M02_MasterTemplete import M02_Doc_Sk_Path as path +from M02_MasterTemplete import M02_Labor_Ref as refs +from M02_MasterTemplete import M02_MasterTemplete_Store as store +from M02_MasterTemplete import M02_Sangeun_Calc as calc +from resources.tester.test_822_1_copy_gap import ( + DIG, + MACHINE, + MAN, + PCT, + _at, + _live, + _move, + _sk, + _stack, + _sum, +) +from resources.tester.test_m02_labor_ref import world # noqa: F401 — 같은 임시 층 + +TOOL = 2 # PCT 「공구손료 및 경장비」 금액 줄 차례 + + +def _doc_move(monkeypatch, key: str, fn) -> None: + """M01 수식 문서 하나 고침(메모리 · 폴더 그대로).""" + live = dc.folder() + new = copy.deepcopy(live[key]) + fn(new) + fake = {**live, key: new} + monkeypatch.setattr(dc, "folder", lambda: fake) + + +def _rate(text: str): + def fn(d: dict) -> None: + d["금액"][TOOL]["단가"] = text + + return fn + + +def _edit(me, key: str, text: str) -> None: + got = _at(me, path.read, key) + doc = copy.deepcopy(got["문서"]) + doc["금액"][TOOL]["단가"] = text + _at(me, path.save, key, {"문서": doc, "판": got["판"]}) + + +def _base(me) -> str: + return _sk(me, 바탕={"로직": PCT}, 단위="㎡") + + +def _labor_unit(me, key: str): + """일위대가 줄이 SK 참조를 펼치는 길(`Expand` → `calc.unit`) 1 단위 계.""" + from M02_MasterTemplete.M02_Labor_Expand import Expand + + with store.at(me): + ref = {"층": "personal", "주인": "7/42", "키": key} + whole = _live() + got = calc.unit(refs.read_ref(ref), whole, Expand(whole), ()) + return got["기준"]["계"] if got["기준"] else None + + +def _list_unit(me, key: str): + """목록 · 옛 셈 길(`calc_doc`) 1 단위 계.""" + with store.at(me): + got = calc.calc_doc(store.read("sangeun", key)["문서"], None, False) + return got["1단위"] + + +@pytest.mark.parametrize("what", ["노임", "재료", "기계"]) +def test_values_follow(world, monkeypatch, what): + """㉠ 값 바뀜 = 묻지 않고 새 금액 · 세 길 같은 답 · 열기 = 파일 안 고침 · 알림 없음.""" + me = world["personal"] + key = _sk(me) + res = {"노임": MAN, "재료": next(iter(_live().index["MT"])), "기계": DIG}[what] + link = MACHINE if what == "기계" else {"갈래": "요소", "키": res} + part = {"노임": "노무비", "재료": "재료비", "기계": "묶음"}[what] + _stack(me, key, [("자원", link, part)]) + was = _sum(me, key) + file = me / "sangeun" / f"{key}.json" + before = file.read_bytes() + _move(monkeypatch, res) + got = _at(me, path.read, key) + assert got["셈"]["계"] > was + assert got["사본"]["자동"] is True and got["사본"]["갱신"] == [] + assert not got["사본"]["단가새값"] and not got["사본"]["M01새판"] + unit = _list_unit(me, key) + assert unit["계"] == pytest.approx(got["셈"]["계"]) + assert _labor_unit(me, key) == pytest.approx(got["셈"]["계"]) + assert file.read_bytes() == before # 읽기 = 작업본 안 씀 + + +def test_logic_follows(world, monkeypatch): + """㉡ 부른 M01 수식 문서(기계 시간당) 바뀜 = 따라옴 · 알림 없음.""" + me = world["personal"] + key = _sk(me) + _stack(me, key, [("기계", MACHINE, "묶음")]) + was = _sum(me, key) + + def twice(d: dict) -> None: + for line in d["금액"]: + line["수량"] = f"({line.get('수량') or 1}) × 2" + + _doc_move(monkeypatch, MACHINE["키"], twice) + got = _at(me, path.read, key) + assert got["셈"]["계"] > was * 1.5 # 율 줄은 누계를 받아 두 배 넘음 + assert got["사본"]["갱신"] == [] and not got["사본"]["문서새판"] + + +def test_edited_item_recopied(world, monkeypatch): + """㉢ 고친 문서 + 그 M01 문서 바뀜 = 고침 지우고 다시 복사 + 알림(전 · 후 · 내 고침) · + [확인](저장) 뒤 알림 없음 · 따로 아님.""" + me = world["personal"] + key = _base(me) + plain = _sum(me, key) + _edit(me, key, "노무비 × 5%") + assert _sum(me, key) > plain + _doc_move(monkeypatch, PCT, _rate("노무비 × 4%")) + got = _at(me, path.read, key) + labor = got["셈"]["노무비"] + assert got["셈"]["경비"] == pytest.approx(int(labor * 0.04), abs=1) + assert got["문서"]["금액"][TOOL]["단가"] == "노무비 × 4%" + [note] = got["사본"]["갱신"] + assert note["키"] == PCT and note["갈래"] == "문서" + assert any("3%" in x and "4%" in x for x in note["M01"]) + assert any("3%" in x and "5%" in x for x in note["내고침"]) + _at(me, path.save, key, {"할일": "갱신확인", "판": got["판"]}) + after = _at(me, path.read, key) + assert after["사본"]["갱신"] == [] and after["사본"]["따로"] is False + assert after["셈"]["계"] == got["셈"]["계"] + + +def test_edit_kept_when_other_item_moves(world, monkeypatch): + """㉣ 고침 + 다른 항목(노임 값)만 바뀜 = 고침 그대로 · 값은 따라옴.""" + me = world["personal"] + key = _base(me) + _edit(me, key, "노무비 × 5%") + was = _at(me, path.read, key)["셈"] + _move(monkeypatch, MAN) + got = _at(me, path.read, key) + assert got["문서"]["금액"][TOOL]["단가"] == "노무비 × 5%" + assert got["사본"]["따로"] is True and got["사본"]["갱신"] == [] + assert got["셈"]["노무비"] > was["노무비"] + + +def test_reverted_is_not_edit(world, monkeypatch): + """㉤ 바꿨다 되돌려 저장 = 안 고침 — M01 바뀌면 조용히 따라옴(알림 없음).""" + me = world["personal"] + key = _base(me) + _edit(me, key, "노무비 × 5%") + _edit(me, key, "노무비 × 3%") + _doc_move(monkeypatch, PCT, _rate("노무비 × 4%")) + got = _at(me, path.read, key) + assert got["사본"]["갱신"] == [] + assert got["문서"]["금액"][TOOL]["단가"] == "노무비 × 4%" + + +def test_project_layer_keeps(world, monkeypatch, tmp_path): + """㉥ 프로젝트 층 사본 = 그대로(M01 바뀌어도 금액 같음 · 고침 그대로).""" + me = world["personal"] + key = _base(me) + _edit(me, key, "노무비 × 5%") + proj = me.parent / "p836" / "templates" + (proj / "sangeun").mkdir(parents=True) + shutil.copy(me / "sangeun" / f"{key}.json", proj / "sangeun" / f"{key}.json") + with store.at(proj): + assert refs.here()[0] == "project" + was = _at(proj, path.read, key) + _move(monkeypatch, MAN) + _doc_move(monkeypatch, PCT, _rate("노무비 × 4%")) + got = _at(proj, path.read, key) + assert got["셈"]["계"] == was["셈"]["계"] and got["사본"]["자동"] is False + assert got["문서"]["금액"][TOOL]["단가"] == "노무비 × 5%" and got["사본"]["갱신"] == [] + + +def test_imported_values_only(world, monkeypatch): + """㉦ 가져온 식 단가기준 우리 = 이음 노임만 따라옴 · 가져온 줄 식 그대로 · 엑셀 기준 = 엑셀 값.""" + from M02_MasterTemplete import M02_Sangeun_Imported as imp + + me = world["personal"] + man = {"종류": "인력", "이름": "보통인부", "단위": "인", "수량": "0.5", "비목": "노무비"} + row = { + "이름": "sub2임시 가져온", + "결과단위": "원/㎡", + "호표": [{**man, "단가": 165545, "이음": MAN}], + } + key = _at(me, lambda: imp.create(row, None, "sub2임시 가져온")["이름"]) + excel = _sum(me, key) + doc = _at(me, lambda: store.read("sangeun", key)["문서"]) + ours = _at(me, lambda: imp.to_price(doc, "우리")["문서"]) + _at(me, lambda: store.write("sangeun", key, store.read("sangeun", key)["판"], ours)) + was = _at(me, path.read, key) + _move(monkeypatch, MAN) + got = _at(me, path.read, key) + assert got["셈"]["계"] == pytest.approx(was["셈"]["계"] * 2, abs=2) + assert got["문서"]["금액"] == was["문서"]["금액"] and got["사본"]["갱신"] == [] + back = _at(me, lambda: imp.to_price(store.read("sangeun", key)["문서"], "엑셀")["문서"]) + _at(me, lambda: store.write("sangeun", key, store.read("sangeun", key)["판"], back)) + assert _sum(me, key) == excel From ea95007a32dd33da28be18a09e86fb20fc73555f Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sun, 15 Nov 2026 13:37:38 +0900 Subject: [PATCH 08/11] =?UTF-8?q?feat(836-2):=20=EA=B8=B0=EA=B3=84=20?= =?UTF-8?q?=EB=8D=AE=EC=9D=8C(836-1)=20=EB=8F=84=20=EA=B3=A0=EC=B9=9C=20?= =?UTF-8?q?=ED=95=AD=EB=AA=A9=20=E2=80=94=20=EA=B7=B8=20=EA=B8=B0=EA=B3=84?= =?UTF-8?q?=20=EC=A4=84=20M01=20=EB=B0=94=EB=80=8C=EB=A9=B4=20=EB=8D=AE?= =?UTF-8?q?=EC=9D=8C=20=EC=A7=80=EC=9B=80=20+=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=C2=B7=20=EB=AC=B8=EC=84=9C=20=EB=8B=A4=EC=8B=9C=20=EB=B3=B5?= =?UTF-8?q?=EC=82=AC=20=EB=95=8C=20=EB=8D=AE=EC=9D=8C=20=EC=9C=A0=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- M02_MasterTemplete/M02_Doc_Sk.py | 2 +- M02_MasterTemplete/M02_Sangeun_Copy.py | 2 +- M02_MasterTemplete/M02_Sk_AutoRenew.py | 71 +++++++++++++++++++- resources/tester/test_836_2_sk_auto_renew.py | 35 ++++++++++ 4 files changed, 107 insertions(+), 3 deletions(-) diff --git a/M02_MasterTemplete/M02_Doc_Sk.py b/M02_MasterTemplete/M02_Doc_Sk.py index 0d532ecbc..d971df7f3 100644 --- a/M02_MasterTemplete/M02_Doc_Sk.py +++ b/M02_MasterTemplete/M02_Doc_Sk.py @@ -131,7 +131,7 @@ def copy_of(sk: dict, ctx: tuple | None) -> dict | None: root = {**dc.md.blank(str(sk.get("키"))), "_틀림": str(e)} return {**got, "문서": root, "부름": {}} try: - root = derive(sk, ctx) + root = renew.carry(got, derive(sk, ctx)) # 맡긴 기계 덮음(836-2) except dc._ERR as e: root = {**dc.md.blank(str(sk.get("키"))), "_틀림": str(e)} values = dc.values_of(sk[skc.KEY]) if skc.has(sk) else dc.values_of(got) # 서버가 정한 값 diff --git a/M02_MasterTemplete/M02_Sangeun_Copy.py b/M02_MasterTemplete/M02_Sangeun_Copy.py index 590cd571a..26e4c9d91 100644 --- a/M02_MasterTemplete/M02_Sangeun_Copy.py +++ b/M02_MasterTemplete/M02_Sangeun_Copy.py @@ -314,7 +314,7 @@ def _formula_job(out: dict, job: Any) -> dict: if got is None or job not in (PRICE, REGEN): return out if job == REGEN: - drop = ("문서", "끊음", "따로", "처음") + drop = ("문서", "끊음", "따로", "처음", "기계덮음") got = {k: v for k, v in got.items() if k not in drop} | {"부름": {}} elif got.get("따로"): got = dc.refresh(got)[0] diff --git a/M02_MasterTemplete/M02_Sk_AutoRenew.py b/M02_MasterTemplete/M02_Sk_AutoRenew.py index 3aed98f79..0f9d8285f 100644 --- a/M02_MasterTemplete/M02_Sk_AutoRenew.py +++ b/M02_MasterTemplete/M02_Sk_AutoRenew.py @@ -26,6 +26,9 @@ from M02_MasterTemplete import M02_Sangeun_Copy as skc from M02_MasterTemplete import M02_Sangeun_Doc as sd from M02_MasterTemplete import M02_Sangeun_Imported as imp +import master_doc_cover as mcv # noqa: E402 — 기계 덮음(836-1 · scripts 길 = dc) + +COVER = mcv.COVER # 문서 칸 기계덮음(836-1) — 따로 문서를 다시 복사할 때 `수식` 머리에 맡겨 둠 FIRST = "처음" # `수식` 칸 — 따로가 된 때의 문서(고침 판정 원본) LOGIC = ( "변수", @@ -136,6 +139,7 @@ def form(sk: Any, got: Any, notes: list | None = None) -> Any: if not isinstance(got, dict) or not on() or not is_sk(sk): return got out = rows(got) if "로직" in got else dict(got) + out = _covers(got, out, notes) if imp.is_imported(sk): return out # 가져온 식 = 값만(식 · 가져온 줄 그대로) live = dc.folder() @@ -164,13 +168,78 @@ def form(sk: Any, got: Any, notes: list | None = None) -> Any: ) if notes is not None: notes.append(_note(key, first, live[key], mark, "문서", mine.get("이름"))) - out = {k: v for k, v in out.items() if k not in ("따로", "문서", FIRST)} + keep = {COVER: mine[COVER]} if mine.get(COVER) else {} # 기계 덮음 = 다른 항목(그대로) + out = {k: v for k, v in out.items() if k not in ("따로", "문서", FIRST, COVER)} | keep if changed and "문서" in out: # 새 문서가 부르는 키 = 닫힌 묶음 keys = [*dc.calls(out["문서"]), *(c for d in cut.values() for c in dc.calls(d))] out["부름"] = dc.closure(dc.over(out), keys, calls) return out +# ── 기계 덮음(836-1) — 그 기계 줄이 M01 에서 바뀌면 덮음 지움 ───────────── +def _eq_rows(copy: dict) -> dict[str, dict]: + """사본의 기계 줄 {원문번호 · 키: 줄}.""" + out: dict[str, dict] = {} + for r in [*(copy.get("로직") or []), *(copy.get("단가") or [])]: + if isinstance(r, dict) and str(r.get("키"))[:2] == "EQ": + out[str(r.get("키"))] = r + if r.get("원문번호"): + out[str(r["원문번호"])] = r + return out + + +def _mine(entry: dict) -> list[str]: + vals = [f"{k} {v}" for k, v in (entry.get("값") or {}).items()] + crew = [f"조종원 {x.get('키')} {x.get('인')}인" for x in entry.get("조종원") or []] + misc = [ + f"잡품 {x.get('이름') or x.get('키') or x.get('줄')} {x.get('율')}%" + for x in entry.get("잡품") or [] + ] + return [*vals, *crew, *misc] or ["덮음"] + + +def _row_diff(a: dict, b: dict) -> list[str]: + va, vb = a.get("값") or {}, b.get("값") or {} + if not isinstance(va, dict) or not isinstance(vb, dict): + return [f"{a.get('값')} → {b.get('값')}"] + keys = [*va, *(k for k in vb if k not in va)] + return [f"{k} {va.get(k, '—')} → {vb.get(k, '—')}" for k in keys if va.get(k) != vb.get(k)] + + +def _covers(got: dict, out: dict, notes: list | None) -> dict: + """문서(따로) · 맡긴 기계 덮음 가운데 그 기계 줄이 M01 에서 바뀐 것 = 지움 + 알림.""" + doc = out.get("문서") if isinstance(out.get("문서"), dict) else None + cover = dict(mcv.cover_of(doc) if doc is not None else out.get(COVER) or {}) + if not cover: + return out + was, now = _eq_rows(got), _eq_rows(out) + for text in list(cover): + a, b = was.get(text), now.get(text) + if a is None or b is None or skc._fp(a) == skc._fp(b): + continue + entry = cover.pop(text) + if notes is not None: + name = f"{b.get('이름') or ''} {b.get('규격') or ''}".strip() or text + notes.append( + { + "키": str(b.get("키")), + "이름": name, + "갈래": "기계", + "M01": _row_diff(a, b) or ["바뀜"], + "내고침": _mine(entry), + } # fmt: skip + ) + if doc is not None: + rest = {k: v for k, v in doc.items() if k != COVER} + return {**out, "문서": {**rest, COVER: cover} if cover else rest} + return {k: v for k, v in out.items() if k != COVER} | ({COVER: cover} if cover else {}) + + +def carry(got: dict, root: dict) -> dict: + """파생 문서에 맡긴 기계 덮음을 다시 얹음(따로 문서를 다시 복사한 뒤).""" + return {**root, COVER: got[COVER]} if isinstance(got.get(COVER), dict) and got[COVER] else root + + def _note(key: str, was: Any, now: dict, mine: list[str], kind: str, name: Any = None) -> dict: return { "키": key, diff --git a/resources/tester/test_836_2_sk_auto_renew.py b/resources/tester/test_836_2_sk_auto_renew.py index 6b52cf995..09f19c86f 100644 --- a/resources/tester/test_836_2_sk_auto_renew.py +++ b/resources/tester/test_836_2_sk_auto_renew.py @@ -211,3 +211,38 @@ def test_imported_values_only(world, monkeypatch): back = _at(me, lambda: imp.to_price(store.read("sangeun", key)["문서"], "엑셀")["문서"]) _at(me, lambda: store.write("sangeun", key, store.read("sangeun", key)["판"], back)) assert _sum(me, key) == excel + + +def _cover(me, key: str, cover: dict) -> None: + got = _at(me, path.read, key) + doc = {**copy.deepcopy(got["문서"]), "기계덮음": cover} + _at(me, path.save, key, {"문서": doc, "판": got["판"]}) + + +def test_machine_cover_dropped_when_machine_moves(world, monkeypatch): + """㉢ 기계 덮음(836-1) + 그 기계 줄 M01 바뀜 = 덮음 지움 + 알림(전 · 후 · 내 고침 값).""" + me = world["personal"] + key = _sk(me) + _stack(me, key, [("기계", MACHINE, "묶음")]) + no = MACHINE["입력"]["기계"].strip("'") + _cover(me, key, {no: {"값": {"가격": 150000}}}) + assert _at(me, path.read, key)["문서"]["기계덮음"] + _move(monkeypatch, DIG) + got = _at(me, path.read, key) + assert "기계덮음" not in got["문서"] + [note] = got["사본"]["갱신"] + assert note["키"] == DIG and note["갈래"] == "기계" + assert any(x.startswith("가격 ") for x in note["M01"]) and note["내고침"] == ["가격 150000"] + + +def test_machine_cover_kept_other_item(world, monkeypatch): + """㉣ 기계 덮음 + 다른 항목(바탕 문서)만 바뀜 = 덮음 그대로(문서 다시 복사 뒤에도).""" + me = world["personal"] + key = _base(me) + _stack(me, key, [("기계", MACHINE, "묶음")]) + no = MACHINE["입력"]["기계"].strip("'") + _cover(me, key, {no: {"값": {"가격": 150000}}}) + _doc_move(monkeypatch, PCT, _rate("노무비 × 4%")) + got = _at(me, path.read, key) + assert got["문서"]["기계덮음"] == {no: {"값": {"가격": 150000}}} + assert [n["갈래"] for n in got["사본"]["갱신"]] == ["문서"] # 기계 줄 붙인 문서 고침 From 943a4ca564417c3a497675afdf013249810375db Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sun, 15 Nov 2026 13:43:51 +0900 Subject: [PATCH 09/11] =?UTF-8?q?chore(ledger):=20=EC=A1=B0=EC=82=AC=20858?= =?UTF-8?q?=20=E2=80=94=20=EA=B8=B0=EA=B3=84=EA=B2=BD=EB=B9=84=20=EA=B8=B0?= =?UTF-8?q?=ED=98=B8=20=ED=91=9C=EA=B8=B0=20=EB=8C=80=EC=A1=B0(=EC=9A=B0?= =?UTF-8?q?=EB=A6=AC=20=C2=B7=20=EA=B2=BD=EC=9F=81=EC=82=AC=20=C2=B7=20?= =?UTF-8?q?=ED=92=88=EC=85=88=20=EC=9B=90=EB=AC=B8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude --- docs/raw/요구_1104/_조사_858_기호표기.md | 249 +++++++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 docs/raw/요구_1104/_조사_858_기호표기.md diff --git a/docs/raw/요구_1104/_조사_858_기호표기.md b/docs/raw/요구_1104/_조사_858_기호표기.md new file mode 100644 index 000000000..522cbd705 --- /dev/null +++ b/docs/raw/요구_1104/_조사_858_기호표기.md @@ -0,0 +1,249 @@ +# 조사 858 — 단가산출 산출근거 기호 표기 (읽기 전용 조사) + +표기: 【사실】 = 파일에서 직접 확인 · 【추정】 = 짐작 · 못 찾음 = 자료 없음. + +## 0. 한눈에 + +| 물음 | 답 | +| --- | --- | +| 경쟁사는 기계경비 안쪽(상각 · 정비 · 관리)을 산출근거에 푸나 | 안 풂 — 손료는 계수 한 수(2,085)만 · 상각 · 정비 · 관리 셋 따로 안 보임 【사실】 | +| 경쟁사 작업량 식 기호 | 품셈 그대로 q · f · K · Cm · E · Q · 뒤에 괄호 한글 뜻 【사실】 | +| 품셈 8장에 기계경비 기호가 있나 | 없음 — 용어(상각비계수 · 정비비계수 · 관리비계수 · 시간당 손료 · 주연료 · 잡재료 · 조종원)만 【사실】 | +| 우리 작업량 식 기호 | 이미 품셈 기호 그대로(GC001147 = q · K · f · E · cm · Q) 【사실】 | +| 사용자가 본 `P_기계` · `k_기계상각비` | 기계경비식 16 문서의 우리 발명 기호 — 품셈에 대응 기호 없음 【사실】 | + +## 1. 경쟁사 표기 (원문 그대로) + +자료 자리 — 저장소 `resources/knowledge/original/실무문서/` 실무 엑셀 (읽기 전용 열람). +- 견본 A · B · C = 울진 대흥 공통 xlsx `2024년 간선임도 신설사업(기번3 울진.울진.대흥.산65외2).xlsx` — 시트 「단가산출근거」 · 「중기사용료」 · 「중기목록표」 (G 칸 `BDCOD|…` 코드 = STmate 내보내기 꼴 【사실】 · 어느 제품 파일인지 문서 표기는 못 찾음 【추정】 STmate) +- 견본 D · E = 거창 xlsx `01.산불거창…/00.25년_산불_거창…내역+수량.xlsx` — 시트 「기계경비」 · 「단가산출」 (같은 폴더 `.estx` 짝 = EST, 시험 `test_812_1_xlsx_golden.py` 머리글 【사실】) + +### 견본 A — STmate 「단가산출근거」 제3호표 (굴삭기 0.7㎥ 작업량 + 비목 줄) + +``` +①기계: 굴삭기 0.7m3 +q (버킷용량) = 0.7 , f (체적환산계수) = 1/1.125 = 0.89 +K (버킷계수) = 0.55 +Cm (사이클시간) = 20 sec (135˚) +E (작업효율) = 0.35 +Q (시간당작업량) = 3600*q*K*f*E/Cm = 21.59 m3/hr +노 무 비 : 55,700 / Q * 2 = 5,159.7 +재 료 비 : 18,001 / Q * 2 = 1,667.5 +경 비 : 26,677 / Q * 2 = 2,471.2 +``` + +### 견본 B — 같은 시트 「덤프트럭 2.5톤 운반」 (시트 145~167행) + +``` +L (운반거리) = 0.1 Km +E (작업효율) = 0.9 , f (체적환산계수) = 1 +q1 (버킷용량) = (2.5/1.7) * 1.0 = 1.47 +n (덤프트럭1대의 토량을 적재하는데 소요되는적재기계의 사이클횟수) +n = q1 / (0.70 * k) = 3.82 회 +t1 (적재시간) = 15 * n / (60 * 0.45) = 2.12 분 +t2 (왕복시간) = (L/10+L/15)* 60 = 1.00 분 +t3 (적하시간) = 0.8 분 · t4 (적재작업이 시작될때까지의시간) = 0.7 분 +Cm (1회 사이클 시간(분)) = t1 + t2 + t3 + t4 = 4.62 분 +Q1 (시간당작업량) = 60 * q1 * f * E / Cm = 17.18 m3/hr +``` + +### 견본 C — STmate 「중기사용료」 (기계 시간당 경비 = 표 5 줄 · 시트 26~32 · 48~54 · 55~60행) + +| 이름 | 규격 | 수량 | 단위 | 단가 | 금액 | +| --- | --- | --- | --- | --- | --- | +| 굴삭기(0.7m3) | 무한궤도 | 0.2085 | 천원 | 110,926 | 23,128 | +| 건설기계운전사 | | 1 | 인 | 55,700 | 55,700 | +| 경유 | 저유황(0.05%) | 11.6 | L | 1,272 | 14,755.2 | +| 잡품 | 주연료의 % | 22 | % | 14,755.2 | 3,246.1 | +| 합 계 | | | | | 96,829 | + +- 굴삭기+브레카(0.7m3) = 굴삭기 0.2085 천원 · 대형브레이카(0.7m3) 0.6601 천원 · 운전사 1 인 · 경유 11.6 L · 잡품 16 % (제7호표) 【사실】 +- 덤프트럭15.0톤 = 0.2279 천원 · 운전사 · 경유 15.9 L · 잡품 38 % (제8호표) 【사실】 +- 손료계수 = 수량 칸에 합계 한 수(2,085 × 10⁻⁷ × 1,000 = 0.2085 천원) · 상각 900 · 정비 700 · 관리 485 따로 안 보임 【사실】 + +### 견본 D — EST 짝 「기계경비」 시트 (거창 xlsx · 4~27행) + +``` +1. 굴삭기(무한궤도) 0.7㎥ + 손 료 117766000 ₩× 2085 × 10(-7) + 주연료 경유 11.6 ℓ × 1376 + 잡 품 주연료의 22 % × 15961.6 + 건설기계운전사 1 인 × 283297 × 1/8*16/12*25/20 +2. 굴삭기(무한궤도) 0.7+브레이카 + 굴삭기손료 117766000 ₩× 2085 × 10(-7) + 브레이카손료 17289000 ₩× 6601 × 10(-7) + 주연료 경유 11.6 ℓ × 1376 · 잡 품 주연료의 16 % × 15961.6 · 건설기계운전사 1 인 … +4. 덤프트럭 15Ton + 손 료 90653000 ₩× 2279 × 10(-7) + 주연료 경유 15.9 ℓ × 1376 · 잡 품 주연료의 38 % × 21878.4 · 건설기계운전사 1 인 … +``` + +- 굴착기 1.0㎥ 짝 = 같은 꼴에 수만 다름: 손료 계수 2,085 · 가격 142,069,000 원 · 경유 19.5 ℓ · 잡품 22 % (`docs/raw/요구_1104/_방향_불러오기.md` N1 · 시트 꼴은 위와 같다고 봄 【추정】) + +### 견본 E — EST 짝 「단가산출」 (거창 xlsx · 197~251행 · 칸 나눠 그린 식) + +``` +q = 0.7 ㎥ K = 0.9 f = 1. / 1.325 +E = 0.75 Cm = 22. sec ( 180 ˚ ) + 3,600. × q × K × f × E +Q = ─────────────────────────── + Cm +노무비 : 59,020 ÷ 57.99 = 1,017.7 +재료비 : 19,473 ÷ 57.99 = 335.7 +경 비 : 24,554 ÷ 57.99 = 423.4 + +2. 운 반 : 덤프트럭 (15Ton) +L = 184.81 m T = 15. ton rt = 1.6 ton / ㎥ +Qt = T ÷ rt × L1 = … n = Qt / (q × K) +t1 = Cms × n / (60. × Es) t2 = (L/7. + L/8.) × 60. ÷ 1,000. t3 = 1.1 분(적하) t4 = 0.7 분(대기) +Cm = t1 + t2 + t3 + t4 + t5 OH = (Cm − t1) / Cm +Q = 60. × Qt × f × E / Cm +``` + +### 경쟁사 표기 규칙 (위 다섯에서 읽은 것) + +| 항목 | 규칙 | +| --- | --- | +| 기호 | 품셈 기호 그대로(q · f · K · Cm · E · Q · L · n · t1~t4 · Qt · OH) · 대문자 Cm 【사실】 | +| 뜻 | STmate = 기호 뒤 괄호 한글 `q (버킷용량) = 0.7` · EST = 뜻 없이 기호만 【사실】 | +| 줄 구성 | `기호 = 식 = 값 단위` 한 줄(견본 A `Q (시간당작업량) = 3600*q*K*f*E/Cm = 21.59 m3/hr`) 【사실】 | +| 기계경비 | 산출근거 안에 안 풂 — 비목 줄은 `단가 / Q` 숫자뿐 · 풀이는 별도 시트(중기사용료 · 기계경비) 【사실】 | +| 기계경비 줄 | 손료(계수 한 수) · 주연료 · 잡품(주연료의 %) · 운전사 — 4~5 줄 · 이름 글만(기호 없음) 【사실】 | +| 상각 · 정비 · 관리 | 산출근거 · 중기사용료에 안 나옴 · STmate 편집창에 입력 칸(계 = 셋 합 · `ui_code_amt.js` `wM_Edit_XRate`) 【사실】 | + +## 2. 품셈 원문 표기 (2026 건설공사 표준품셈 · 제8장) + +원문 폴더 = `resources/knowledge/original/원가계산/건설공사_표준품셈/본문/01_공통부문/제08장_건설기계/` + +### 작업량 식 기호 + +| 기호 | 뜻 | 원문 자리 | +| --- | --- | --- | +| Q = n·q·f·E | 시간당 작업량 · n 시간당 작업싸이클 수 · q 1회 싸이클당 표준작업량 · f 체적환산계수 · E 작업효율 | `8-01_적용기준.md` 8-1-4 | +| n = 60/cm(min) 또는 3,600/cm(sec) | cm = 싸이클시간(소문자) | 8-1-4 [주]⑤ | +| E = 현장 작업능력계수 × 실작업시간율 | | 8-1-4 [주]⑥ | +| Q = 3,600·q·k·f·E / cm | q 버킷용량(㎥) · f · E · K 버킷계수 · cm 1회 싸이클 시간(초) — 식은 소문자 k · 풀이는 대문자 K | `8-02-03_굴착기.md` | +| Q = 60·q·f·E / cm · q = qo×e | q 삽날 용량 · qo 거리 안 본 삽날 용량 · e 운반거리계수 · cm 1회 싸이클 시간 | `8-02-01_불도저.md` | +| Q = 60·q·f·E / cm · q = T/γt·L | T 적재용량(ton) · γt 습윤밀도 · L 체적변화율 · E 0.9 · cm = t1+…+t6 | `8-02-08_덤프트럭.md` | + +### 기계경비 말(기호 없음) + +| 말 | 뜻 | 원문 자리 | +| --- | --- | --- | +| 취득가격 · 평균취득가격 | 평균취득가격 = 취득가격×(1.1×내용년수+0.9)/(2×내용년수) | 8-1-5 6 · 7 | +| 시간당 손료 | 취득가격 × 시간당 손료계수 합계(원 미만 절사) | 8-1-5 12 | +| 기계손료 = 상각비 + 정비비 + 관리비 | 관리비는 1일 8시간 상한 | 8-1-6 2 | +| 기계가격(천원) | 굴착기(무한궤도) 1.0㎥ = 142,069 | `8-05_기계가격.md` | +| 내용시간 · 연간표준가동시간 · 상각비율 · 정비비율 · 연간관리비율 | 10,000 · 1,250 · 0.9 · 0.7 · 0.1 | `8-03-01_[00]토공기계.md` (0201) 표 머리 | +| 시간당(10⁻⁷) 상각비계수 · 정비비계수 · 관리비계수 · 계 | 900 · 700 · 485 · 2,085 | 같은 표 (0201-0100) | +| 주연료(ℓ/hr) · 잡재료(주연료의 %) · 조종원(인/일) | 19.5 · 22 · 1 | `8-04_운전경비_산정.md` 8-4-1 | +| 가산비율(%) | 암석 20 · 전석섞인토사 10 (굴착기 무한궤도) | 8-1-7 1 | + +## 3. 우리 표기 사실 + +### 기호가 정해지는 길 + +| 자리 | 내용 | +| --- | --- | +| 자료 | `resources/master_data/수식문서/수식_*.json` 59 파일 · 1,520 문서 · 변수 19,209 — 변수마다 `기호` · `아래`(아래첨자) · `위` · `이름` · `단위` 칸 【사실】 | +| 글자 만들기 | 화면은 `기호_아래^위` 로 합쳐 TeX 로 그림(`A00_Common/line_stack_tex.ts` `symTex`) · 한글 아래첨자는 `\text{}` 로 그림 【사실】 | +| 기본 기호 규칙 | `resources/master_data/scripts/master_symbol.py` — 이름 끝말로 머리글자(계수 k · 율 r · 단가 P · 노임 w · 수량 Q …) + 아래 = 이름 알맹이 6 자 · 원문 기호(`변수표_796_3.json`)와 사용자가 정한 칸은 덮지 않음 【사실】 | +| 표 값 이름 | `tableAlias`(`common_util/common_util_line_stack_pick.ts` 213행)가 `Q_분자^표` · `Q_분모^표` 를 조건 글 `'시간환산'` 따라 `Q_시간환산2` · `Q_시간환산3` 으로 바꿔 그림 — 화면의 `Q_시간환산2 = 1 · Q_시간환산3 = 8` 이 이것 【사실】 | +| id 글 | 식 · 금액 글 안에서 변수를 `기호_아래` 글로 부름(예 `P_기계 × 1000 × ((k_기계상각비 + …`) 【사실】 | +| 호출 문서 | 부르는 쪽 입력 이음은 `열쇠`(기계 · 지역 · 보정작업)로 넘김 — 기호 글에 안 기댐(GC000089 의 `P_굴착기무한궤` 연결로 확인) 【사실】 | +| 기호 글을 부르는 다른 코드 · 시험 | `k_기계상각비` · `P_기계주연료` · `Q_시간환산` 은 수식문서 json 3 파일에만 있음(코드 · 시험 0) · 프로젝트 storage 사본은 안 봄 — 못 찾음 | + +### 기계경비식 (GC000267 등 · `부품.갈래 = 기계경비식`) + +- 16 문서(건설 8장 14 + 산림 2) · 변수 243 · 서로 다른 기호꼴 64 · 아래첨자 없는 변수 0 · 품셈 원문 기호와 같은 것 0(품셈에 기호가 없음) 【사실】 +- GC000267 변수 대응 + +| 우리 기호 | 뜻(원문 말) | 원문 기호 | +| --- | --- | --- | +| P_기계 | 취득가격(천원) | 없음 | +| k_기계상각비 · k_기계정비비 · k_기계관리비 | 시간당 상각비계수 · 정비비계수 · 관리비계수 | 없음 | +| C_손료 | 시간당 손료 | 없음 | +| r_가산 | 가산비율(8-1-7) | 없음 | +| P_기계주연료 | 주연료(ℓ/hr) — 양인데 가격 글자 P | 없음 | +| Q_기계조종원 | 조종원(인/일) | 없음 | +| r_기계잡재료 | 잡재료(주연료의 %) | 없음 | +| Q_시간환산 | 조종원 노임 시간 환산 = 분자/분모 표값 1/8 | 없음(우리 이음) | +| S_기계 · S_지역 · S_보정작업 · S_연료 · S_직종 | 고르기 칸 | 없음 | +| w_기계운전사 · P_자동차용경유 | 노임 · 유가 | 없음 | + +### 작업량 식 (GC001143 · GC001147 · 시공능력 41 문서 · 변수 654) + +| 갈래 | 개수 | 예 | +| --- | --- | --- | +| 원문 기호 그대로(아래 없음) | 233 (36 %) | cm · q · f · E · n · Q · L · e · t | +| 원문 기호 + 숫자 첨자 | 92 (14 %) | q_0 · V_1 · V_2 · t_1 · t_2 · L_1 · L_2 · E_2 | +| 우리 고르기 칸 `S_…` | 111 (17 %) | S_종별 · S_현장조건 · S_등급 · S_토질명 | +| 우리 표 값 칸 `…^표` | 110 (17 %) | Q_보정값 · v_속도 · T_시간 · k_cm작업거리 | +| 기호 + 한글 아래(우리 이름) | 69 (11 %) | θ_선회 · X_싸이클 · N_발톱 · L_주행 · V_적재함 · W_단위 | +| 우리 변형 | 39 (6 %) | E_입력 · E_보정 · r_보정 | + +- 원문 기호와 다른 것 = 329 / 654 (50 %) — 거의 전부 원문에 기호가 없는 고르기 · 표 값 · 입력 칸 【사실】 +- GC001147 굴착기 작업량 식 = 품셈과 같은 `Q = 3600 × q × K × f × E ÷ cm` (cm · K 모양 그대로) 【사실】 +- `Cm`(대문자) = 산림품셈 문서 24 변수 · `cm`(소문자) = 건설 8장 18 변수 — 경쟁사 · 산림품셈은 Cm · 건설 8장 원문은 cm 【사실】 +- 대표 예 20 (원문 ↔ 우리) + +| # | 원문 | 우리 | +| --- | --- | --- | +| 1 | q 버킷용량 | q (같음) | +| 2 | f 체적환산계수 | f (같음) | +| 3 | E 작업효율 | E (같음) · 입력 E_입력 · 보정 E_보정 · 표값 E_2 | +| 4 | K 버킷계수 | K (같음) | +| 5 | cm 싸이클시간 | cm (같음 · 경쟁사 Cm) | +| 6 | Q 시간당 작업량 | Q (같음) | +| 7 | n 시간당 싸이클 수 | n (같음) | +| 8 | qo 거리 안 본 삽날 용량 | q_0 | +| 9 | e 운반거리계수 | e (같음) | +| 10 | V1 · V2 전진 · 후진속도 | V_1 · V_2 | +| 11 | t1 ~ t6 | t_1 ~ t_6 | +| 12 | L 운반거리 | L (같음) · 덤프에서는 L 이 체적변화율 · 거리는 L_운반 | +| 13 | γt 습윤밀도 | W_단위 | +| 14 | 취득가격 | P_기계 | +| 15 | 상각비계수 | k_기계상각비 | +| 16 | 정비비계수 | k_기계정비비 | +| 17 | 관리비계수 | k_기계관리비 | +| 18 | 시간당 손료 | C_손료 | +| 19 | 주연료(ℓ/hr) | P_기계주연료 | +| 20 | 조종원 · 잡재료 · 가산비율 | Q_기계조종원 · r_기계잡재료 · r_가산 | + +## 4. 대조 표 — 굴착기(무한궤도) 시간당 경비 + +값은 우리 화면 기준 굴착기 1.0㎥(P 142,069 · 900 / 700 / 485 · 경유 19.5 ℓ · 잡 22 %). + +| 항목 | ㉠ 지금 우리 | ㉡ 경쟁사(EST 기계경비 꼴) | ㉢ 품셈 원문 말 | +| --- | --- | --- | --- | +| 가격 | `P_기계 = 142,069` | `손 료 142,069,000 ₩×` (원 단위로 적음) | 취득가격(천원) 142,069 | +| 계수 | `k_기계상각비 = 900 · k_기계정비비 = 700 · k_기계관리비 = 485` | `2085 × 10(-7)` (한 수) | 상각비계수 900 · 정비비계수 700 · 관리비계수 485 · 계 2,085 | +| 손료 식 | `C_손료 = P_기계 × 1,000 × ((k_기계상각비 + k_기계정비비) × (1 + r_가산/100) + k_기계관리비) / 10,000,000 = 29,621` | (식 없음 · 가격 × 계수 줄 한 줄) | 시간당 손료 = 취득가격 × 시간당 손료계수 합계 · 가산비율 8-1-7 | +| 연료 | `P_기계주연료 = 19.5` · `주연료 : 1,846.39 × 19.5 ℓ = 36,004.6` | `주연료 경유 19.5 ℓ × 1376` | 주연료 19.5 ℓ/hr | +| 잡품 | `r_기계잡재료 = 22` | `잡 품 주연료의 22 % × …` | 잡재료 22 (주연료의 %) | +| 조종원 | `Q_기계조종원 = 1 · Q_시간환산 = 1/8` | `건설기계운전사 1 인 × 노임 × 1/8*16/12*25/20` | 조종원 1 (인/일) | +| 작업량 | `Q = 3600 × q × K × f × E ÷ cm` | `Q (시간당작업량) = 3600*q*K*f*E/Cm` | `Q = 3,600·q·k·f·E / cm` | + +- 조종원 시간환산: 경쟁사 `1/8*16/12*25/20`(= 0.2083) ↔ 우리 `1/8` — 값 차이는 이 조사 범위 밖 · 사실만 적음 【사실】 + +## 5. 「깔끔한」 표기 안 + +| 안 | 값 줄 | 식 줄 | +| --- | --- | --- | +| 안 1 품셈 말 그대로 | `취득가격 142,069 천원 · 상각비계수 900 · 정비비계수 700 · 관리비계수 485` | `손료 = 취득가격 × 1,000 × ((상각비계수 + 정비비계수) × (1 + 가산비율/100) + 관리비계수) / 10⁷ = 29,621` | +| 안 2 짧은 한글 이름(사용자 예) | `가격 142,069 천원 · 상각 900 · 정비 700 · 관리 485` | `손료 = 가격 × 1,000 × ((상각 + 정비) × (1 + 가산/100) + 관리) / 10⁷ = 29,621` | +| 안 3 경쟁사 줄 꼴 | `손료 142,069,000 원 × 2,085 × 10⁻⁷ = 29,621` / `주연료 경유 19.5 ℓ × 1,846.39` / `잡품 주연료의 22 %` / `조종원 1 인 × …` | 식 줄 없이 비목 줄만 — 상각 · 정비 · 관리 · 가산비율이 안 보임 | + +작업량 식(q · K · f · E · cm · Q)은 세 안 모두 지금 그대로 — 품셈 기호가 이미 쓰임. + +### 안별 바뀌는 범위 + +| 안 | 자료 파일 | 그리는 코드 | 영향 문서 | 메모 | +| --- | --- | --- | --- | --- | +| 그리기만 바꿈(안 1 · 2 공통 방법) | 0 | `A00_Common/line_stack_tex.ts`(기호 → 글) · `common_util/common_util_line_stack_pick.ts`(`tableAlias` 옆 그림 전용 사전) 2 파일 + 시험 | 기계경비식 16 문서 + 그것을 펼쳐 보이는 484 문서의 펼친 블록 | 식 · 열쇠 · 계산 불변 · 왼쪽 영역 · 복사(`formula_doc_copy.ts`)가 같은 사전을 타는지는 못 확인 【추정】 | +| 자료를 바꿈(안 1 · 2) | `수식_건설품셈_08장_건설기계.json` 14 문서 + 산림 02장 · 10장 각 1 문서(변수 243 · 식 · 금액 글 안 id 포함) | 없음(그림은 기호 글을 그대로 그림) | 16 문서 · 부르는 쪽은 열쇠 이음이라 불변 | 마스터 규칙 `master_symbol.py` 가 한글 기호를 「기호 꼴」로 안 쳐서 빌더 재실행 때 되돌릴 가능성 【추정】 · 사용자 마스터 편집 길과 겹침 | +| 안 3 줄 꼴 | 0~16 문서(금액 줄 · 수식 구성 바꿈) | 기계경비식 전용 그림 틀 신설 | 16 문서 + 펼친 484 | 상각 · 정비 · 관리 · 가산비율 식이 가려져 계산 검증성 하락 | + +## 6. 판정 한 줄 + +그림 전용 사전으로 안 1(품셈 말 그대로)을 먼저 보이고 안 2 와 견줌 — 자료 0 건 · 되돌리기 쉬움 · 결정은 사용자 몫. From 00514598f0166d185ad49e62927404b24dbcb55e Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sun, 15 Nov 2026 13:45:18 +0900 Subject: [PATCH 10/11] =?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)): From 8e411d732e4f3c311a25685278d62ba76b00825f Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sun, 15 Nov 2026 13:49:49 +0900 Subject: [PATCH 11/11] =?UTF-8?q?chore(ledger):=20=EC=9A=94=EA=B5=AC=20860?= =?UTF-8?q?=20=EC=9E=A5=EB=B6=80=20=E2=80=94=20=EA=B8=B0=ED=98=B8=20?= =?UTF-8?q?=EC=95=88=202=20=EC=A7=84=ED=96=89=20=C2=B7=20=EC=A1=B0?= =?UTF-8?q?=EC=A2=85=EC=9B=90=20=EC=8B=9C=EA=B0=84=ED=99=98=EC=82=B0=20?= =?UTF-8?q?=EC=A1=B0=EC=82=AC=20=C2=B7=20=EC=9E=AC=EB=A3=8C=20=EB=8B=A8?= =?UTF-8?q?=EC=9C=84=20=EC=B0=A8=EC=9D=B4=20=EC=A1=B0=EC=82=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude --- docs/raw/요구_1104/사용자요구_1104.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/raw/요구_1104/사용자요구_1104.md b/docs/raw/요구_1104/사용자요구_1104.md index 7eaf0695b..11e75b067 100644 --- a/docs/raw/요구_1104/사용자요구_1104.md +++ b/docs/raw/요구_1104/사용자요구_1104.md @@ -1378,3 +1378,9 @@ GC000089의 단가품셈 기초의 항목을 내가 하나 삭제했음. 되돌 추가 업무 [Image #1]1. 여기에서 표 값에 항목과 아래 인력 항목에 중복의로 표 선택지를 보여주고 있음. 내용은 업어도 될듯. 2. [Image #2] 여기에서 좌측 굴착기 항목이 품번으로 나오고 우측에 계산 값이 없는데 괜찮은 건지 확인 + +## 요구 860 (11-15 · 사용자 · ① 기호 표기 안 2(짧은 한글 이름)로 진행 · 보고 판단 ② 기계 조종원 1/8 = 8 시간 기준 — 품셈에서 해당 내용 조사(기계 부분 노임이 낮게 나옴) ③ 일위대가 불러오기에서 경쟁사 단가와 우리 단가화 비교 시 재료 구매 단위 차이로 값이 완전히 달라지는 경우 조사) + +1. 안2로 진행하고 보고 판단할께. +2. 우리기계의 1/8이 결국은 8시간 기준으로 했다는건데 품셈에서 해당내용을 좀 찾아봐. 대체로 기계부분의 노임 계산이 좀 낮게 나오고 있음. 조사 +3. 일위대가 불러오기에서 경쟁사 단가와 우리단가화를 비교해보면 재료의 구매 단위 차이로 인해 값이 완전히 달라지는 경우가 있음. 조사