fix(822-1): 사본 빈틈 막기 — 셈이 읽은 M01 줄을 저장 때 값 사본에 박음 · 가져온 식 우리 = 사본 · 빈틈 표지
- 새 M02_Doc_Gap: 문서 셈이 읽은 요소 · 표 줄 가운데 값 사본에 없는 것(gaps) → 지금 M01 닫힌 묶음으로 더함(fill) - 수식 길(M02_Doc_Sk_Path.edited): 줄 쌓기 품셈 기계 · 바꿔 끼운 기계 · 자원 줄 · 단가산출 참조 · 내 기계 모두 저장 때 박음(옛 _mine 대신) - 저장 첫머리(M02_Doc_Sk.settle): 따로 = [저장] · [단가 갱신] 때 빈틈 메움 · 파생 = 옛 로직 사본에 더함 - 가져온 식 단가기준 우리(M02_Doc_Import): 셈 · 연결 기록 = 로직 사본 위(지금 M01 직접 읽던 빈틈) · 엑셀 기준 그대로 - 읽기 답 사본 표지 「사본없음」 n · 화면 배지 「사본 없는 줄 n」(지금 값으로 셈 · 저장하면 굳음) · 일괄 고쳐 쓰지 않음 - 시험 test_822_1_copy_gap 14 · storage SK 122 고치기 전후 금액 같음 · 빈틈 0 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
* 층 쓰기 권한 없으면 「읽기만」 배지
|
||||
* (821-3) 값 줄만 바뀜(M01 단가 · 내 기계 고침) = 「새 값 있음」(같은 자리 · 같은 꼴) · 지운 키(M01 · 내 기계) = 「지워짐 n」 ·
|
||||
* [단가 갱신] 확인 창 = 바뀐 자원 줄(이름 · 바뀐 칸 전 → 후 · 지워짐 = 마지막 값 유지)
|
||||
* (822-1) 값 사본에 없는 셈 줄(옛 저장본 빈틈) = 「사본 없는 줄 n」(지금 값으로 셈 · 저장하면 굳음)
|
||||
* ========================================================================== */
|
||||
|
||||
import { createBadge } from "@ui/ui_template_badge";
|
||||
@@ -23,6 +24,8 @@ export interface CopyMark {
|
||||
M01새판?: boolean;
|
||||
지운키?: string[];
|
||||
단가새값?: boolean;
|
||||
/** 값 사본에 없는 셈 줄 수(822-1) — 지금 값으로 셈 · 저장하면 굳음 */
|
||||
사본없음?: number;
|
||||
문서새판?: string[];
|
||||
따로?: boolean;
|
||||
/** 가져온 식(엑셀 불러오기 · 807-1) 단가기준 — 엑셀 = 엑셀 값 기억 · 우리 = 연결 값 */
|
||||
@@ -64,6 +67,12 @@ export function copyBadges(m: CopyMark | undefined, edit: boolean): HTMLElement[
|
||||
b.dataset.copyGone = m.지운키.join(",");
|
||||
out.push(b);
|
||||
}
|
||||
if (m?.사본없음) {
|
||||
const b = createBadge(`${ft("Gap")} ${m.사본없음}`, "warning");
|
||||
b.title = ft("Gap_Note");
|
||||
b.dataset.copyGap = String(m.사본없음);
|
||||
out.push(b);
|
||||
}
|
||||
if (!edit) out.push(createBadge(ft("Read_Only"), "neutral"));
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -66,6 +66,8 @@ const TEXT = {
|
||||
New_Value: ["새 값 있음", "New values"],
|
||||
Gone: ["지워짐", "Deleted"],
|
||||
Gone_Note: ["마지막 값 유지", "last value kept"],
|
||||
Gap: ["사본 없는 줄", "Uncopied rows"],
|
||||
Gap_Note: ["저장하면 지금 값으로 굳음", "saving freezes current values"],
|
||||
Price: ["단가 갱신", "Refresh prices"],
|
||||
Regen: ["재생성", "Regenerate"],
|
||||
Price_Ask: ["자원 단가만 지금 M01 값으로 — 식 · 연결 그대로", "Prices only — equation unchanged"],
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
"""M02 단가산출(SK) 수식 사본 빈틈(PLAN 822-1 · 요구 797-1) — 서버 단독.
|
||||
|
||||
빈틈 = 문서 셈이 읽은 M01 요소 · 표 줄 가운데 값 사본에 없는 것(줄 쌓기 기계 · 바꿔 끼운 기계 ·
|
||||
더한 자원 줄 · 단가산출 참조가 읽는 노임 · 유가 · 내 기계 …) — 사본 색인이 지금 M01 로 흘러
|
||||
M01 이 바뀌면 금액이 바로 따라 바뀜.
|
||||
`gaps` = 빈틈 키(셈 한 번 · 읽은 줄 적기) · `fill` = 빈틈을 지금 M01 닫힌 묶음으로 값 사본에 더함
|
||||
(있는 줄 그대로 — `skc.grow`) — [저장] · [단가 갱신] 때만(열기 · 미리 셈은 지금 값으로 셈 + 표지).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from M02_MasterTemplete import M02_Doc_Copy as dc
|
||||
from M02_MasterTemplete import M02_MyMachine as mym
|
||||
from M02_MasterTemplete import M02_Sangeun_Copy as skc
|
||||
|
||||
|
||||
class _Seen:
|
||||
"""셈 색인 겉 — `get` · `value` · `logic` 이 내준 줄 키를 적음(그 밖 = 속 색인 그대로)."""
|
||||
|
||||
def __init__(self, inner: Any) -> None:
|
||||
self._inner, self.keys = inner, set()
|
||||
|
||||
def get(self, ref: str) -> dict:
|
||||
return self._note(self._inner.get(ref))
|
||||
|
||||
def value(self, ref: str):
|
||||
self.get(ref)
|
||||
return self._inner.value(ref)
|
||||
|
||||
def logic(self, key: str) -> dict:
|
||||
return self._note(self._inner.logic(key))
|
||||
|
||||
def _note(self, row: Any) -> Any:
|
||||
if isinstance(row, dict) and row.get("키"):
|
||||
self.keys.add(str(row["키"]))
|
||||
return row
|
||||
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
return getattr(self._inner, name)
|
||||
|
||||
|
||||
def _have(copy: dict) -> set[str]:
|
||||
return {str(r.get("키")) for r in [*(copy.get("로직") or []), *(copy.get("단가") or [])]}
|
||||
|
||||
|
||||
def gaps(copy: dict) -> list[str]:
|
||||
"""값 사본에 없는데 문서 셈이 읽은 M01 줄 키 + 문서의 내 기계 키(셈 틀림 = 읽은 데까지)."""
|
||||
if "로직" not in copy or not isinstance(copy.get("문서"), dict):
|
||||
return []
|
||||
seen = _Seen(dc.over(copy))
|
||||
try:
|
||||
dc.mc.calc(copy["문서"], dc.ml.Links(seen, dc.docs_of(copy)))
|
||||
except dc._ERR:
|
||||
pass
|
||||
have = _have(copy)
|
||||
return sorted({*seen.keys, *mym.keys_in(copy["문서"])} - have)
|
||||
|
||||
|
||||
def fill(copy: dict) -> dict:
|
||||
"""빈틈을 지금 M01(+ 지금 층 내 기계) 닫힌 묶음으로 값 사본에 더함 · 빈틈 없음 = 그대로."""
|
||||
keys = gaps(copy)
|
||||
if not keys:
|
||||
return copy
|
||||
return {**copy, **skc.grow(dc.values_of(copy), skc.snap([], keys))}
|
||||
@@ -51,6 +51,16 @@ def basis_of(sk: dict) -> str:
|
||||
return "우리" if (sk.get(imp.KEY) or {}).get("단가기준") == "우리" else "엑셀"
|
||||
|
||||
|
||||
def _index(sk: dict, master: Any = None) -> Any:
|
||||
"""셈 색인 — 우리 = 로직 사본 위(822-1 · 이음 값이 M01 따라 안 바뀜 · [단가 갱신] 으로만) ·
|
||||
엑셀 = 받은 색인 · 없으면 지금 M01."""
|
||||
from M02_MasterTemplete import M02_Sangeun_Copy as skc
|
||||
|
||||
if basis_of(sk) == "우리":
|
||||
return skc.over(sk, imp._whole())
|
||||
return master or imp._whole()
|
||||
|
||||
|
||||
# ── 엑셀 단가 그대로 엔진 줄(옛 셈 길 · SK 고침 얹은 것) ───────────────────
|
||||
def _ctx(sk: dict, whole: Any) -> tuple:
|
||||
"""엑셀 단가 기준 고친 줄 — 옛 셈 길(`OLD`)로 돌려 엔진에 넣은 (색인, 키, 입력, 답, 재료)."""
|
||||
@@ -514,7 +524,7 @@ def record(doc: dict, sk: dict) -> dict[str, Any]:
|
||||
import master_doc_record as mr # `M02_Doc_Copy` 가 scripts 를 길에 둔 뒤
|
||||
|
||||
basis = basis_of(sk)
|
||||
links = Links(imp._whole(), basis)
|
||||
links = Links(_index(sk), basis)
|
||||
mine = view(doc, basis)
|
||||
rec = mr.Rec(links)
|
||||
try:
|
||||
@@ -529,7 +539,7 @@ def record(doc: dict, sk: dict) -> dict[str, Any]:
|
||||
def run(doc: dict, sk: dict, master: Any = None) -> dict:
|
||||
"""가져온 식 문서 셈(SK 단가기준) — 계산기 = M01 과 같은 `master_doc_calc`."""
|
||||
basis = basis_of(sk)
|
||||
links = Links(master or imp._whole(), basis)
|
||||
links = Links(_index(sk, master), basis)
|
||||
mine = view(doc, basis)
|
||||
try:
|
||||
links.learn(mine)
|
||||
|
||||
@@ -22,6 +22,7 @@ from decimal import Decimal
|
||||
from typing import Any
|
||||
|
||||
from M02_MasterTemplete import M02_Doc_Copy as dc
|
||||
from M02_MasterTemplete import M02_Doc_Gap as gap
|
||||
from M02_MasterTemplete import M02_Doc_Import as dim
|
||||
from M02_MasterTemplete import M02_Sangeun_Copy as skc
|
||||
from M02_MasterTemplete import M02_Sangeun_Doc as sd
|
||||
@@ -196,8 +197,10 @@ def settle(out: dict, whole: Any, job: Any, raw: Any) -> dict:
|
||||
"수식 문서로 고친 단가산출 — 옛 칸(덮개 · 줄) 고침은 수식 탭에서 · 또는 [재생성]",
|
||||
409,
|
||||
)
|
||||
copy = dc.refresh(have)[0] if job == skc.PRICE and not dim.is_imported(out) else have
|
||||
return {**out, KEY: norm(copy)}
|
||||
if dim.is_imported(out):
|
||||
return {**out, KEY: norm(have)}
|
||||
copy = dc.refresh(have)[0] if job == skc.PRICE else have
|
||||
return {**out, KEY: norm(gap.fill(copy))} # 사본 빈틈 = 지금 값으로 박음(822-1)
|
||||
# 빈 SK(바탕 · 줄 없음) = 빈 문서(요구서_664 0 장) · 수식 길로 고친 빈 SK = 위 따로(804-4)
|
||||
if not skc.has(out) and not dim.is_imported(out):
|
||||
empty = seed(out)
|
||||
@@ -207,4 +210,7 @@ def settle(out: dict, whole: Any, job: Any, raw: Any) -> dict:
|
||||
calls = {} if job == skc.REGEN else (have or {}).get("부름") or {}
|
||||
first = {**(dc.values_of(out[skc.KEY]) if skc.has(out) else {}), "부름": calls}
|
||||
copy = copy_of({**out, KEY: first}, box[-1] if box else None)
|
||||
if copy and skc.has(out) and (keys := gap.gaps(copy)): # 파생 — 옛 로직 사본에 박음(822-1)
|
||||
out = {**out, skc.KEY: skc.grow(out[skc.KEY], skc.snap([], keys))}
|
||||
copy = {**copy, **dc.values_of(out[skc.KEY])}
|
||||
return {**out, KEY: norm({k: v for k, v in (copy or first).items() if k != "따로"})}
|
||||
|
||||
@@ -17,11 +17,11 @@ import json
|
||||
from typing import Any
|
||||
|
||||
from M02_MasterTemplete import M02_Doc_Copy as dc
|
||||
from M02_MasterTemplete import M02_Doc_Gap as gap
|
||||
from M02_MasterTemplete import M02_Doc_Import as dim
|
||||
from M02_MasterTemplete import M02_Doc_Screen as screen
|
||||
from M02_MasterTemplete import M02_Doc_Sk as dsk
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
||||
from M02_MasterTemplete import M02_MyMachine as mym
|
||||
from M02_MasterTemplete import M02_Sangeun_Copy as skc
|
||||
from M02_MasterTemplete import M02_Sangeun_Doc as sd
|
||||
|
||||
@@ -77,7 +77,11 @@ def answer(copy: dict, sk: dict) -> dict[str, Any]:
|
||||
got = dsk.run(sk, copy)
|
||||
head = {k: copy.get(k) for k in ("시점", "단가시점")}
|
||||
mark = {**head, "따로": bool(copy.get("따로"))}
|
||||
mark |= {"단가기준": dim.basis_of(sk)} if dim.is_imported(sk) else dc.state(copy)
|
||||
mark |= (
|
||||
{"단가기준": dim.basis_of(sk)}
|
||||
if dim.is_imported(sk)
|
||||
else {**dc.state(copy), "사본없음": len(gap.gaps(copy))} # 저장하면 지금 값으로 굳음
|
||||
)
|
||||
body = {
|
||||
"문서": copy["문서"],
|
||||
"부름": copy.get("부름") or {},
|
||||
@@ -129,22 +133,12 @@ def edited(copy: dict, body: dict, key: str, sk: dict | None = None) -> dict:
|
||||
*(c for d in out.get("끊음", {}).values() for c in dc.calls(d)),
|
||||
]
|
||||
if not dim.is_imported(sk):
|
||||
out |= _mine(out)
|
||||
out["부름"] = dc.closure(dc.over(out), calls, copy.get("부름"))
|
||||
out = gap.fill(out) # 셈이 읽은 줄 가운데 사본 없는 것 = 지금 값으로 박음(822-1)
|
||||
out["따로"] = True
|
||||
return out
|
||||
|
||||
|
||||
def _mine(copy: dict) -> dict:
|
||||
"""줄 쌓기에서 고른 내 기계(기계='개인 EQP…') 가운데 값 사본에 없는 것만 지금 값으로 더함
|
||||
(821-3) — 그 뒤 내 기계를 고쳐도 금액 그대로 + 「단가새값」 · [단가 갱신] 으로 반영 ·
|
||||
지워도 마지막 값."""
|
||||
keys = mym.keys_in(copy.get("문서"))
|
||||
if not keys or "로직" not in copy:
|
||||
return {}
|
||||
return skc.grow(dc.values_of(copy), skc.snap([], keys))
|
||||
|
||||
|
||||
def preview(key: str, body: dict) -> dict[str, Any]:
|
||||
"""미리 셈(저장 없음) — {셈, 전, 후, 바뀐자원?, 버림?}."""
|
||||
doc = _file(key)["문서"]
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
# ruff: noqa: F811 — 임시 층 fixture(world)를 test_m02_labor_ref 에서 받아 씀
|
||||
"""822-1 사본 빈틈 막기(요구 797-1) — 길마다: 만들기 → [저장] → M01 값(임시 · 메모리 덮개)을
|
||||
기계 가격 · 노임 · 유가 · 자재 값으로 바꿈 → 금액 그대로 + 「단가새값」 → [단가 갱신] → 반영.
|
||||
|
||||
길 = 수식 길 줄 쌓기(품셈 기계 · 바꿔 끼운 기계 · 자원 줄 · 단가산출 참조) ·
|
||||
옛 로직 길(더한줄 로직 · 재료 · 인력) · 가져온 식(단가기준 우리) · 층 복사 · [재생성] ·
|
||||
빈틈 SK 표시(`사본없음`).
|
||||
M01 바꿈 = `skc.Copy` 아닌 진짜 색인 사본(`dc.base` 가 못 벗김) · 정본 파일 안 고침.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import json
|
||||
|
||||
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_Labor_Ref as refs
|
||||
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 — 같은 임시 층
|
||||
|
||||
DIG, DIG_NO, DIG_LOGIC = "EQ000022", "0201-0100", "GC000267" # 굴착기(무한궤도) 1.0㎥
|
||||
DIG2_NO = "0201-0070" # 바꿔 끼울 굴착기
|
||||
MAN = "LB000002" # 보통인부
|
||||
PCT = "GC001109" # 금속기와 해체(인력 둘 + 노무비 3 %)
|
||||
|
||||
|
||||
def _live():
|
||||
return m01.loaded()[1]
|
||||
|
||||
|
||||
def _move(monkeypatch, *keys: str) -> None:
|
||||
"""M01 값 두 배 — 키마다 값 칸(기계 = 가격 · 자재 = 물가 칸) · 진짜 색인 사본(Copy 아님) ·
|
||||
수식 문서 폴더 그대로."""
|
||||
files, live = m01.loaded()
|
||||
fake = copy.copy(live)
|
||||
fake.index, fake.numbers = dict(live.index), dict(live.numbers)
|
||||
for key in keys:
|
||||
tid = key[:2]
|
||||
row = copy.deepcopy(live.index[tid][key])
|
||||
if tid == "EQ":
|
||||
row["값"]["가격"] *= 2
|
||||
elif tid == "MT":
|
||||
for k in ("물가자료", "유통물가", "물가정보", "거래가격"):
|
||||
row[k] = row[k] * 2 if row.get(k) else row.get(k)
|
||||
else:
|
||||
row["값"] *= 2
|
||||
fake.index[tid] = {**fake.index[tid], key: row}
|
||||
if row.get("원문번호"):
|
||||
fake.numbers[tid] = {**fake.numbers[tid], str(row["원문번호"]): row}
|
||||
monkeypatch.setattr(m01, "loaded", lambda: (files, fake))
|
||||
|
||||
|
||||
def _at(folder, fn, *args):
|
||||
with store.at(folder):
|
||||
return fn(*args)
|
||||
|
||||
|
||||
def _sk(folder, **body) -> str:
|
||||
body = {"이름": "sub1임시 빈틈", "단위": "시간", "더한줄": [], **body}
|
||||
return _at(folder, lambda: refs.create("sangeun", body)["이름"])
|
||||
|
||||
|
||||
def _var(sym: str, link: dict, name: str = "") -> dict:
|
||||
return {"기호": "P", "아래": sym, "위": "", "이름": name or sym, "단위": "", "연결": link}
|
||||
|
||||
|
||||
def _line(sym: str, part: str = "경비") -> dict:
|
||||
return {"이름": sym, "단위": "hr", "비목": part, "수량": "1", "단가": f"P_{sym}"}
|
||||
|
||||
|
||||
def _stack(folder, key, rows: list[tuple[str, dict, str]]) -> None:
|
||||
"""줄 쌓기 — 지금 문서에 (기호, 연결, 비목) 줄을 더해 수식 길로 저장."""
|
||||
got = _at(folder, path.read, key)
|
||||
doc = copy.deepcopy(got["문서"])
|
||||
doc.setdefault("변수", []).extend(_var(s, link) for s, link, _ in rows)
|
||||
doc.setdefault("금액", []).extend(_line(s, part) for s, _, part in rows)
|
||||
_at(folder, path.save, key, {"문서": doc, "판": got["판"]})
|
||||
|
||||
|
||||
def _sum(folder, key):
|
||||
return _at(folder, path.read, key)["셈"]["계"]
|
||||
|
||||
|
||||
def _price(folder, key) -> dict:
|
||||
"""[단가 갱신] 저장 — 바뀐 자원 키 · 뒤 금액."""
|
||||
look = _at(folder, path.preview, key, {"할일": skc.PRICE})
|
||||
판 = _at(folder, path.read, key)["판"]
|
||||
_at(folder, path.save, key, {"할일": skc.PRICE, "판": 판})
|
||||
return {"바뀐": sorted(x["키"] for x in look["바뀐자원"]), "후": _sum(folder, key)}
|
||||
|
||||
|
||||
MACHINE = {"갈래": "단가산출", "키": DIG_LOGIC, "입력": {"기계": f"'{DIG_NO}'"}}
|
||||
|
||||
|
||||
def _fuel_and_crew(no: str = DIG_NO) -> tuple[str, str]:
|
||||
"""기계 시간당 식이 읽는 유가(MO) · 조종원 노임(LB) 키."""
|
||||
w = _live()
|
||||
row = w.numbers["EQ"][no]
|
||||
fuel = w.numbers["MO"][f"{row['값']['주연료종류']}:전국평균"]["키"]
|
||||
crew = w.numbers["LB"][str(row["값"]["조종원직종"])]["키"]
|
||||
return fuel, crew
|
||||
|
||||
|
||||
# ── 수식 길 줄 쌓기 ───────────────────────────────────────────────────
|
||||
@pytest.mark.parametrize("what", ["가격", "유가", "노임"])
|
||||
def test_stack_m01_machine(world, monkeypatch, what):
|
||||
"""품셈 기계(굴착기 1.0㎥) 줄 쌓기 → 저장 → 기계 가격 · 유가 · 조종원 노임 바뀜 →
|
||||
금액 그대로 + 단가새값 → [단가 갱신] → 반영."""
|
||||
me = world["personal"]
|
||||
key = _sk(me)
|
||||
_stack(me, key, [("기계", MACHINE, "묶음")])
|
||||
was = _sum(me, key)
|
||||
assert was > 0
|
||||
fuel, crew = _fuel_and_crew()
|
||||
moved = {"가격": DIG, "유가": fuel, "노임": crew}[what]
|
||||
_move(monkeypatch, moved)
|
||||
now = _at(me, path.read, key)
|
||||
assert now["셈"]["계"] == was and now["사본"]["단가새값"] is True
|
||||
got = _price(me, key)
|
||||
assert got["바뀐"] == [moved] and got["후"] > was
|
||||
|
||||
|
||||
def test_stack_swapped_machine(world, monkeypatch):
|
||||
"""바꿔 끼운 기계 — 기계 입력을 다른 굴착기로 바꿔 저장 → 그 기계 값이 사본에."""
|
||||
me = world["personal"]
|
||||
key = _sk(me)
|
||||
_stack(me, key, [("기계", MACHINE, "묶음")])
|
||||
got = _at(me, path.read, key)
|
||||
doc = copy.deepcopy(got["문서"])
|
||||
for v in doc["변수"]:
|
||||
if (v.get("연결") or {}).get("키") == DIG_LOGIC:
|
||||
v["연결"]["입력"] = {"기계": f"'{DIG2_NO}'"}
|
||||
_at(me, path.save, key, {"문서": doc, "판": got["판"]})
|
||||
was = _sum(me, key)
|
||||
other = _live().numbers["EQ"][DIG2_NO]["키"]
|
||||
_move(monkeypatch, other)
|
||||
assert _sum(me, key) == was
|
||||
assert _price(me, key)["바뀐"] == [other]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("kind", ["인력", "재료"])
|
||||
def test_stack_resource(world, monkeypatch, kind):
|
||||
"""자원 줄 더하기(노임 · 자재 요소) — 저장 뒤 값 바뀌어도 그대로 · 갱신 뒤 반영."""
|
||||
me = world["personal"]
|
||||
res = MAN if kind == "인력" else next(iter(_live().index["MT"]))
|
||||
key = _sk(me)
|
||||
part = "노무비" if kind == "인력" else "재료비"
|
||||
_stack(me, key, [("자원", {"갈래": "요소", "키": res}, part)])
|
||||
was = _sum(me, key)
|
||||
assert was > 0
|
||||
_move(monkeypatch, res)
|
||||
assert _sum(me, key) == was
|
||||
assert _price(me, key)["바뀐"] == [res]
|
||||
|
||||
|
||||
def test_stack_logic_ref(world, monkeypatch):
|
||||
"""단가산출 참조(M01 단가산출을 부름) — 그 식이 읽는 노임도 사본에."""
|
||||
me = world["personal"]
|
||||
key = _sk(me)
|
||||
_stack(me, key, [("참조", {"갈래": "단가산출", "키": PCT, "입력": {}}, "묶음")])
|
||||
was = _sum(me, key)
|
||||
assert was > 0
|
||||
_move(monkeypatch, MAN)
|
||||
assert _sum(me, key) == was
|
||||
assert MAN in _price(me, key)["바뀐"]
|
||||
|
||||
|
||||
# ── 옛 로직 길(더한줄 · 파생) ────────────────────────────────────────
|
||||
OLD_ADDS = {
|
||||
"로직 기계": ([{"종류": "로직", "로직": DIG_LOGIC, "입력값": {"기계": DIG_NO}, "수량": 1}],
|
||||
{"기계": DIG_NO}, DIG),
|
||||
"재료 요소": ([{"종류": "재료", "요소": "MT000008", "수량": 1}], {}, "MT000008"),
|
||||
"인력 요소": ([{"종류": "인력", "요소": MAN, "수량": 1}], {}, MAN),
|
||||
} # fmt: skip
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", list(OLD_ADDS))
|
||||
def test_old_added_lines(world, monkeypatch, name):
|
||||
"""옛 로직 길 더한줄(로직 기계 · 재료 요소 · 인력 요소) — 저장 뒤 그대로 · 갱신 뒤 반영."""
|
||||
me = world["personal"]
|
||||
adds, given, moved = OLD_ADDS[name]
|
||||
key = _sk(me, 더한줄=adds, 입력값=given)
|
||||
was = _sum(me, key)
|
||||
assert was > 0
|
||||
assert _at(me, path.read, key)["사본"]["사본없음"] == 0
|
||||
_move(monkeypatch, moved)
|
||||
assert _sum(me, key) == was
|
||||
got = _price(me, key)
|
||||
assert moved in got["바뀐"] and got["후"] > was
|
||||
|
||||
|
||||
# ── 가져온 식 · 층 복사 · 재생성 ──────────────────────────────────────
|
||||
def test_imported_ours(world, monkeypatch):
|
||||
"""가져온 식 단가기준 우리 — 이음 노임 바뀌어도 그대로 · 갱신 뒤 반영 · 엑셀 기준 = 엑셀 값."""
|
||||
from M02_MasterTemplete import M02_Sangeun_Imported as imp
|
||||
|
||||
me = world["personal"]
|
||||
man = {"종류": "인력", "이름": "보통인부", "단위": "인", "수량": "0.5", "비목": "노무비"}
|
||||
row = {
|
||||
"이름": "sub1임시 가져온",
|
||||
"결과단위": "원/㎡",
|
||||
"호표": [{**man, "단가": 165545, "이음": MAN}],
|
||||
}
|
||||
key = _at(me, lambda: imp.create(row, None, "sub1임시 가져온")["이름"])
|
||||
assert _sum(me, key) == 82772 # 엑셀 단가 그대로
|
||||
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 = _sum(me, key)
|
||||
_move(monkeypatch, MAN)
|
||||
assert _sum(me, key) == was
|
||||
doc = _at(me, lambda: store.read("sangeun", key)["문서"])
|
||||
got = _at(me, skc.preview, doc, skc.PRICE)
|
||||
assert [x["키"] for x in got["바뀐자원"]] == [MAN] and got["후"]["계"] > was
|
||||
|
||||
|
||||
def test_layer_copy_keeps_copy(world, monkeypatch):
|
||||
"""층 복사(개인 → 회사) — 줄 쌓기 기계 사본째 · 원본 M01 값 바뀐 뒤 복사해도 그 값."""
|
||||
me, co = world["personal"], world["company"]
|
||||
key = _sk(me)
|
||||
_stack(me, key, [("기계", MACHINE, "묶음")])
|
||||
was = _sum(me, key)
|
||||
_move(monkeypatch, DIG)
|
||||
ref = {"층": "personal", "주인": "7/42", "키": key}
|
||||
with store.at(co):
|
||||
doc = refs.read_ref(ref)
|
||||
new = refs.create("sangeun", refs.carry(refs.origin_mark(doc, ref), ("personal", "7/42")))
|
||||
assert _sum(co, new["이름"]) == was
|
||||
assert _at(co, path.read, new["이름"])["사본"]["단가새값"] is True
|
||||
|
||||
|
||||
def test_regen_follows_m01(world, monkeypatch):
|
||||
"""[재생성] — 지금 M01 로 다시 찍음(따로 문서 버림) · 그 뒤 M01 바뀌어도 그대로."""
|
||||
me = world["personal"]
|
||||
key = _sk(me, 바탕={"로직": PCT}, 단위="㎡")
|
||||
_stack(me, key, [("기계", MACHINE, "묶음")])
|
||||
_move(monkeypatch, MAN)
|
||||
판 = _at(me, path.read, key)["판"]
|
||||
_at(me, path.save, key, {"할일": skc.REGEN, "판": 판})
|
||||
after = _at(me, path.read, key)
|
||||
assert after["사본"]["따로"] is False and after["사본"]["사본없음"] == 0
|
||||
assert not after["사본"]["단가새값"]
|
||||
was = after["셈"]["계"]
|
||||
_move(monkeypatch, MAN) # 한 번 더(네 배)
|
||||
assert _sum(me, key) == was
|
||||
|
||||
|
||||
def test_gap_mark_then_save_fills(world, monkeypatch):
|
||||
"""빈틈 있는 옛 SK — 열면 지금 값으로 셈 + 「사본없음 n」 · 일괄 고쳐 쓰지 않음 ·
|
||||
[저장] 하면 지금 값으로 굳음(n = 0) · 그 뒤 M01 바뀌어도 그대로."""
|
||||
me = world["personal"]
|
||||
key = _sk(me)
|
||||
_stack(me, key, [("기계", MACHINE, "묶음")])
|
||||
file = me / "sangeun" / f"{key}.json"
|
||||
raw = json.loads(file.read_text(encoding="utf-8"))
|
||||
vals = raw["문서"]["수식"] if "문서" in raw else raw["수식"]
|
||||
for part in ("로직", "단가"):
|
||||
vals[part] = [] # 옛 저장본 흉내 — 줄 쌓기 기계를 사본에 안 박은 때
|
||||
file.write_text(json.dumps(raw, ensure_ascii=False), encoding="utf-8")
|
||||
before = file.read_bytes()
|
||||
got = _at(me, path.read, key)
|
||||
assert got["사본"]["사본없음"] > 0
|
||||
was = got["셈"]["계"]
|
||||
assert file.read_bytes() == before # 열기 = 안 고쳐 씀
|
||||
files, live = m01.loaded()
|
||||
_move(monkeypatch, DIG)
|
||||
moved = _sum(me, key)
|
||||
assert moved > was # 빈틈 = 지금 값으로 셈
|
||||
got = _at(me, path.read, key)
|
||||
_at(me, path.save, key, {"문서": got["문서"], "판": got["판"]})
|
||||
now = _at(me, path.read, key)
|
||||
assert now["사본"]["사본없음"] == 0 and now["셈"]["계"] == moved
|
||||
monkeypatch.setattr(m01, "loaded", lambda: (files, live)) # 앞 값(절반)
|
||||
assert _sum(me, key) == moved
|
||||
Reference in New Issue
Block a user