Files
Aislo/resources/tester/test_723_4_b09_sc_qty.py
T
eomsangdonandClaude Opus 5.5 9633e97c3c feat(723-4): 723-1 뒤 B09 일위대가 수량 연결 마무리 — 알약 값 쓴 SC 만 덮개 나눔 · SK 속 SC · 공사 불러오기 수량 · 비고 경고
- Labor_Source = 덮개 열쇠 `C:참조|S:사본` 은 알약 값 쓴 SC 블록만(빈 수량 · 옛 손 = `C:참조` 그대로 = 703-1 지난 공사 가져옴 꼴) ·
  SK 속 SC 안 줄 수량 없음 = 엔진엔 0 값묶음(`_Loose`) · 그 줄 = 사전조합 블록(이 공사 입력) — SK 통째 빈칸 안 됨
- Bill_Hopyo(707-7 공사 통째 불러오기) = 이 공사 엑셀 수량을 굳힌 손으로 남김(723-1 `with_pills` 수량 걷기는 M02 저장용)
- Daega_Personal 원천 = 블록 origin 참조(덮개 열쇠 꼬리 무관) · 내 자료집 저장 알림 글 = 줄 수량 저장 안 함
- 비고(펼침 · 일위대가표) = 수량 없는 SC 안 줄은 「산근 n호표 · 수량 없음 — 이 공사에서 입력」(`noteOf`)
- 낡은 시험 고침(사유 = 723-1 SC 안 줄 수량 저장 안 함 · 이 공사 입력 · 알약으로 이음):
  test_204_2(서고 SC 수량 없음 · ask) · test_703(자료집 저장 손 걷힘) · test_719_1(서고 사본 = 알약 값으로 같은 값 · 없으면 수량 없음) ·
  test_719_2(SK 속 SC = 빈 줄 → 이 공사 입력 1 = M02 알약 1 금액) · test_702_3(서고 SC 빈 줄 3 → 입력 뒤 금액)
- test_723_4 = 구조물 사본 면적 알약 굳힘 · 설계 연동 · 알약 안 이음 = 빈 줄 시험 더함

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 22:25:30 +09:00

157 lines
7.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""723-4 B09 공사 사본의 일위대가(SC) 안 줄 수량 = 프로젝트 값 연결.
- 723-1 = M02 일위대가(SC) 안 줄 · 자재 수량 저장 안 함(null = 수량 없음 · 1 아님)
- 구조물 조합에서 온 SC = 그 사본 면적 알약 값(「참조 id#차례」 · 「참조 id#자재|코드」)으로 굳힘
- 내역에서 바로 쓴 SC(서고 사본) = 빈 줄 `ask` → 이 공사에서 입력(702 덮개 qty) → 금액
- 옛 손(B09 굳힌 사본) = 그대로 · 덮개 열쇠 `C:참조` 그대로(기존 공사 금액 · 덮개 같음) ·
알약 값 쓴 블록만 `C:참조|S:사본`(사본마다 값 다름 · 이 공사 입력 덮개와 안 섞임)
- 셈 짝(`B09_Estimation_Calc_Labor` .py ↔ .ts) 거울
"""
import importlib.util
import json
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from B09_Estimation.B09_Estimation_Calc_Labor import compute_labor # noqa: E402
from B09_Estimation.B09_Estimation_Labor_Source import ASK, Builder # noqa: E402
from M02_MasterTemplete import M02_Labor_Ref as refs # noqa: E402
_spec = importlib.util.spec_from_file_location(
"b145", Path(__file__).with_name("test_145_2_b09_labor_calc.py")
)
b145 = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(b145)
REF = {"층": "project", "주인": "t", "키": "SC-000001"}
RID = refs.ref_id(REF)
MAT = "MT00001"
SHEETS = {"labor": [], "material": [{"key": MAT, "price": 500}], "machine": []}
def _fix(name, labor):
return {"고정": {"이름": name, "단위": "식", "단가": {"노무비": labor}}}
def _doc(hand=None, mat=None):
"""SC 한 벌 — 고정 줄 둘(노무 1000 · 2000) + 자재 하나 · 손 = hand · mat(None = 수량 없음)."""
q = (lambda v: {"수량": {"손": v}}) if hand is not None else (lambda v: {"수량": None})
return {
"이름": "시험 SC",
"단위": "㎡",
"담은로직": [{**_fix("갑", 1000), **q(hand)}, {**_fix("을", 2000), **q(hand)}],
"자재": [{"코드": MAT, "수량": {"손": mat} if mat is not None else None}],
}
def _build(doc, use=({}, "")):
build = Builder()
whole = {MAT: {"이름": "철선", "규격": "#8", "단위": "kg"}} # 사본 단가 대신(_material 만 읽음)
bid = build.precombo(whole, REF, doc, {}, 1, use)
block = build.blocks[bid]
return {"blocks": [block], "owners": {"pipe@100.0": bid}}, block
def test_수량_없는_SC_서고_사본_경고():
daega, block = _build(_doc(), ({}, "L:lib"))
assert [ln["qty"] for ln in block["lines"]] == [None, None, None]
assert all(ln["ask"] == ASK for ln in block["lines"])
assert block["cover"] == f"C:{RID}" # 공사 안 한 덮개(703-1 지난 공사 가져옴 꼴 그대로)
out = compute_labor(daega, SHEETS)
got = out["daega_blocks"][0]
assert got["blank"] == 3 and got["sum"] == 0
assert [ln["why"] for ln in got["lines"]] == [ASK] * 3
assert sum(g["why"] == ASK for g in out["gaps"]) == 3
assert out["daega"][0]["blank"] == 3 # 내역 · 원가계산서 빈 줄(166-6 · 701-3)로 넘어감
def test_이_공사에서_입력_금액():
daega, block = _build(_doc(), ({}, "L:lib"))
cids = [ln["cid"] for ln in block["lines"]]
cover = {block["cover"]: {cids[0]: {"qty": 2}, cids[1]: {"qty": 0.5}, cids[2]: {"qty": 3}}}
got = compute_labor(daega, SHEETS, None, cover)["daega_blocks"][0]
assert [ln["amount"] for ln in got["lines"]] == [2000.0, 1000.0, 1500.0]
assert got["blank"] == 0 and got["sum"] == 4500.0
assert not any("why" in ln for ln in got["lines"]) # 입력 뒤 경고 글 없음
def test_구조물_조합_알약_값_굳힘():
given = {f"{RID}#0": 1.5, f"{RID}#1": 2, f"{RID}#자재|{MAT}": 4}
daega, block = _build(_doc(), (given, "S:pipe@100.0"))
assert [ln["qty"] for ln in block["lines"]] == [1.5, 2.0, 4.0]
assert not any("ask" in ln for ln in block["lines"])
assert block["cover"] == f"C:{RID}|S:pipe@100.0"
got = compute_labor(daega, SHEETS)["daega_blocks"][0]
assert got["sum"] == 1500.0 + 4000.0 + 2000.0 and got["blank"] == 0
def test_옛_손_그대로():
daega, block = _build(_doc(hand=3, mat=2), ({}, "S:pipe@100.0"))
assert [ln["qty"] for ln in block["lines"]] == [3.0, 3.0, 2.0]
assert block["cover"] == f"C:{RID}" # 옛 덮개 열쇠 그대로
got = compute_labor(daega, SHEETS)["daega_blocks"][0]
assert got["sum"] == 3000.0 + 6000.0 + 1000.0
def test_거울_파이썬_TS():
daega, block = _build(_doc(), ({}, "L:lib"))
cid = block["lines"][0]["cid"]
for cover in ({}, {block["cover"]: {cid: {"qty": 2}}}):
py = json.loads(json.dumps(compute_labor(daega, SHEETS, {}, cover), ensure_ascii=False))
ts = b145._ts(daega, SHEETS, {}, cover)
assert b145._same(py, ts["out"])
def _structure(tmp_path, area):
"""구조물 사본(참조 SC 한 줄 · 굳힌 SC = 수량 없음) + 면적.json(SC 안 줄 알약 연결 · 723-1)."""
from M02_MasterTemplete import M02_Item_Snapshot as snapshots
root = b145._project(tmp_path / "proj", [(100, "SS000901")])
sc = {**_doc(), "자재": []}
b145._snap(root, "pipe@100.0", "SS000901", [{"참조": REF, "수량": {"손": 1}}], {}, {RID: sc})
folder = snapshots.structures_dir(root) / snapshots.folder_name("pipe@100.0")
if area is not None:
(folder / "면적.json").write_text(json.dumps(area, ensure_ascii=False), encoding="utf-8")
return root, folder
def _sc_block(root):
from B09_Estimation.B09_Estimation_Labor_Source import daega_inputs
got = daega_inputs(root)
combo = next(b for b in got["blocks"] if b["id"] == got["owners"]["pipe@100.0"])
return got, next(b for b in got["blocks"] if b["id"] == combo["lines"][0]["key"])
def test_구조물_사본_면적_알약_값_굳힘_설계_연동(tmp_path):
links = [{"칸": "A1", "키": f"{RID}#0"}, {"칸": "A2", "키": f"{RID}#1"}]
area = {"시트": [{"id": "s", "칸": {"A1": {"값": 1.5}, "A2": {"값": 2}}, "연결": links}]}
root, folder = _structure(tmp_path, area)
got, block = _sc_block(root)
assert [ln["qty"] for ln in block["lines"]] == [1.5, 2.0]
assert block["cover"] == f"C:{RID}|S:{folder.name}" and not any(
"ask" in ln for ln in block["lines"]
)
out = compute_labor(got, SHEETS)
by_id = {b["id"]: b for b in out["daega_blocks"]}
assert by_id[block["id"]]["sum"] == 1500.0 + 4000.0
assert by_id[got["owners"]["pipe@100.0"]]["sum"] == 5500.0 # 조합 = SC 1 단위 × 1
# 설계 연동 — 면적 값이 바뀌면 다시 셈(사본 면적.json)
area["시트"][0]["칸"]["A1"]["값"] = 3
(folder / "면적.json").write_text(json.dumps(area, ensure_ascii=False), encoding="utf-8")
assert [ln["qty"] for ln in _sc_block(root)[1]["lines"]] == [3.0, 2.0]
def test_구조물_사본_알약_안_이으면_빈_줄(tmp_path):
root, folder = _structure(tmp_path, None)
got, block = _sc_block(root)
assert [ln["qty"] for ln in block["lines"]] == [None, None]
assert block["cover"] == f"C:{RID}" # 알약 안 씀 = 서고 SC 와 같은 「이 공사 입력」 덮개
out = compute_labor(got, SHEETS)
owner = next(r for r in out["daega"] if r["key"] == got["owners"]["pipe@100.0"])
assert owner["blank"] >= 1 # 부른 SC 빈 줄 = 조합도 빈 줄(166-6)