- 빌더 build_기계_인력줄_842_3.py(규칙 = master_machine_crew.from_old_row · 다시 돌려도 같음) - 인력 줄 301 대(1 인 282 · 2 인 18 · 7 인 1 · 직종 빈 줄 42 = 빈 키 = 식 입력 직종 고르기 그대로) - 잡품 줄 328 대(잡재료율 > 0 = 주연료 · 전력 줄마다 그 율 · 「73.7+」 글 연료도 줄) · 범위 글 「13∼18」 2 대 = 잡품 없음(값원문 그대로) - 선원류 칸 79 대 · 비활성 표지 79 대 그대로 · 옛 칸 남음 0 - 바탕 덮음 = 식에 그 줄이 있을 때만(손료만 식을 일부러 부른 단가산출 GC000324 → GC000273 크롤러드릴 = 조종원 안 더함) - 식 고름 = 그 줄 인력도 조종원 있음으로 봄 · 로직 검사 · 값 모으기 = 기계 칸을 목록 읽기로(master_formula_check) - 시험 고침: 821_2 · 822_2 미리보기 = 목록 읽은 값으로 · 822_1 노임 키 = 인력 줄 · 822_2 범위 까닭 글 = 「잡재료율 없음」(범위 글은 값원문) - 전후: 655 대 시간당(hourly · brief · 문서 셈) 653 같음 · 다름 2 = 펌프 준설선 EQ000552 · 553(비활성 · 범위 글) 까닭 글만 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
288 lines
12 KiB
Python
288 lines
12 KiB
Python
# 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 — 같은 임시 층
|
|
|
|
|
|
@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" # 보통인부
|
|
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"][row["값"]["인력"][0]["키"].removeprefix("LB:")]["키"] # 842-3 인력 줄
|
|
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
|