Files
Aislo/resources/tester/test_222_1_m02_apart.py
eomsangdonandClaude Sonnet 5.5 2d9a469fc7 feat(806-1): 구조물 조합 일위대가 보기 = 일위대가 컨테이너 한 부품 · 단가산출 줄 = 그 SK 문서 모달(수식 탭 · 저장 = 그 SK)
- 조합 상세 · 카드 · 서고 모두 Labor_Table 한 부품 — 조합 전용 칸(면적 잇기 · 수량산출 모달 · 규격 열 · 다이어그램)은 새 Labor_Combo
- 단가산출(SK) 참조 줄 누름 = 새 Labor_SkEdit — 그 SK 문서를 단가산출 상세 부품(수식 탭 첫 화면)으로 열고 [저장] = 수식 저장 → SK 문서 저장 → 일위대가 금액 다시 셈 · 위 층 SK = 읽기만 · 두 번 눌러도 모달 하나
- 옛 조합 전용 표 · 단가산출 보기 모달(Labor_SangeunModal) 지움 · 전국 자재 줄은 Labor_Table 이 그림
- 새 시험 test_806_1_labor_view · 소스 지킴 시험 12 개를 새 자리(Labor_Table · Labor_RowModal · Labor_SkEdit)로 고침 — 옛 Detail 자리 문구가 없어져 낡았음

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-08 16:31:49 +09:00

156 lines
8.0 KiB
Python

"""M02 구조물조합 일위대가 기계비빔타설(PLAN 222-1) — 단위 까닭 · 별도계상 빼기 · 원문 여럿.
① 단위 — GF000156 모래 · 자갈 = 원문 kg(QF000353) ÷ 단위중량(㎏/㎥) · 칸에 t/㎥ 값(2 · 2.1)을 넣으면
838 ÷ 2 = 419 ㎥ 처럼 1000 배 · 0 = 표 위끝(1,700 · 1,800 ㎏/㎥) → 0.49 · 0.57 ㎥ · 칸을 비우면
자동값(master_auto 「나누는 자리 = 1」)이 1 이라 838 ㎥(자료 · 기본값 몫 — 223 보고).
② 별도계상 — 로직 자재(`자재표시` · 빈 칸 = 기본 별도계상) = 기준에서 빠지고 `줄` 에서 지움 · `재료` 는
남음 · 사용자 자재 = 합에서 뺌(짝 TS `settle`) · 사전조합 안 줄 · 사본 셈 같은 길.
③ 원문 — 로직 출처 + 호표 · 입력 출처 가운데 같은 책 절 번호(「산림품셈 12-1-2 · 12-1-3」).
"""
import os
from decimal import Decimal
import pytest
from M01_MasterData import M01_MasterData_Store as m01
from M01_MasterData import M01_MasterData_Store_Make as make
from M02_MasterTemplete import M02_Labor_Doc as labor
from M02_MasterTemplete import M02_Labor_Inputs as inputs
from M02_MasterTemplete import M02_Labor_Ref as refs
from resources.tester.test_m02_labor_calc_mirror import _same, _ts
HERE = os.path.dirname(os.path.abspath(__file__))
SRC = os.path.join(HERE, "..", "..", "M02_MasterTemplete")
MIX = "GF000156" # 기계비빔타설 — 호표 3 시멘트 · 4 모래 · 5 자갈
GIVEN = {"재료배합": "(A)", "골재최대치수": 40}
MAT = "MT000008"
COSTS = ("노무비", "재료비", "경비")
DIRECT = {"별도계상": "직접계산"}
def _whole():
return m01.loaded()[1]
def _rows(got):
return {x["이름"]: {**x, "수량": float(x["수량"])} for x in got["줄"]}
def _kept(raw, names=None):
"""별도계상 뺀 기준(751-3 끝수) = 남은 줄 비목 합마다 원 버림 · 계 = 셋 합 ·
`names` = 뺄 재료 이름(없으면 재료 전부)."""
gone = {m["차례"] for m in raw["재료"] if names is None or m["이름"] in names}
lines = [x for n, x in enumerate(raw["줄"]) if n not in gone]
out = {k: int(sum(Decimal(str(x["비목"].get(k) or 0)) for x in lines)) for k in COSTS}
return {**out, "계": sum(out.values())}
# ── ① 단위 ──────────────────────────────────────────────────────────
def test_단위중량_칸에_t_값이면_1000배_0이면_표_위끝():
wrong = labor.run_line(_whole(), MIX, {**GIVEN, "모래단위중량": 2, "자갈단위중량": 2.1})
rows = _rows(wrong)
assert rows["시멘트"]["단위"] == "kg" and rows["시멘트"]["수량"] == pytest.approx(335)
assert rows["모래(건조상태)"]["수량"] == pytest.approx(838 / 2) # 419 ㎥
assert rows["자갈 또는 부순돌"]["수량"] == pytest.approx(1032 / 2.1) # 491.4 ㎥
zero = {**GIVEN, "모래단위중량": 0, "자갈단위중량": 0}
right = _rows(labor.run_line(_whole(), MIX, zero))
assert right["모래(건조상태)"]["수량"] == pytest.approx(838 / 1700)
assert right["자갈 또는 부순돌"]["수량"] == pytest.approx(1032 / 1800)
# ── ② 별도계상 ───────────────────────────────────────────────────────
def _calc(shown=None, mats=()):
line = {"로직": MIX, "입력값": GIVEN, "수량": {"손": 1}}
if shown:
line["자재표시"] = shown
doc = labor.clean({"열": "rv", "담은로직": [line], "자재": list(mats)})
return labor.calc(doc, {})
def test_로직_자재_기본_별도계상은_기준_줄에서_빠지고_재료엔_남음():
raw = labor.run_line(_whole(), MIX, GIVEN)
one = _calc()["줄"][0]
names = {x["이름"] for x in one["줄"]}
assert not names & {"시멘트", "모래(건조상태)", "자갈 또는 부순돌"}
assert {"콘크리트공", "보통인부"} <= names
assert len(one["재료"]) == 3 # 자재목록(세 칸 고침)엔 남음
want = _kept(raw)
assert {k: one["기준"][k] for k in (*COSTS, "계")} == want
assert one["기준"]["노무비"] == raw["기준"]["노무비"]
def test_직접계산은_그대로_섞이면_그_자재만_빠짐():
raw = labor.run_line(_whole(), MIX, GIVEN)
full = _calc({str(n): DIRECT for n in (3, 4, 5)})["줄"][0]
assert full["기준"]["계"] == pytest.approx(float(raw["기준"]["계"]))
assert len(full["줄"]) == len(raw["줄"])
part = _calc({"3": DIRECT})["줄"][0] # 시멘트만 셈
names = {x["이름"] for x in part["줄"]}
assert "시멘트" in names and "모래(건조상태)" not in names
others = {m["이름"] for m in raw["재료"]} - {"시멘트"}
assert part["기준"]["계"] == _kept(raw, others)["계"]
def test_사용자_자재_별도계상은_합에서_빠지고_반영은_보임():
aside = _calc(None, [{"코드": MAT, "수량": {"손": 2}}])
direct = _calc(None, [{"코드": MAT, "수량": {"손": 2}, **DIRECT}])
price = direct["자재"][0]["기준단가"]
assert aside["자재"][0]["반영"] == pytest.approx(price * 2) # 목록엔 금액 보임
assert direct["반영합"]["계"] - aside["반영합"]["계"] == pytest.approx(price * 2)
assert direct["기준합"]["재료비"] - aside["기준합"]["재료비"] == pytest.approx(price)
@pytest.mark.parametrize("mark", [None, "직접계산", "별도계상", "옛값"])
def test_사용자_자재_별도계상_파이썬과_TS_같은_답(mark):
one = {"코드": MAT, "수량": {"손": 3}, **({"별도계상": mark} if mark else {})}
doc = labor.clean({"열": "rv", "담은로직": [{"로직": MIX, "입력값": GIVEN}], "자재": [one]})
doc["자재"][0].update({"별도계상": mark} if mark else {}) # 옛 값도 그대로 넘겨 봄
answer = labor.calc(doc, {})
values = {"기계비빔타설|㎥": 2}
_same(labor.settle(answer, doc, values), _ts(answer, doc, values))
def test_사전조합_안_줄도_별도계상_뺌():
raw = labor.run_line(_whole(), MIX, GIVEN)
doc = {
"이름": "비빔 조합",
"단위": "㎥",
"담은로직": [{"로직": MIX, "입력값": GIVEN, "수량": {"손": 1}}],
"자재": [
{"코드": MAT, "수량": {"손": 2}}, # 기본 별도계상 = 안 셈 · 안 줄에도 없음
],
}
ref = {"층": "system", "주인": None, "키": "SC999999"}
got = refs.Expand(_whole(), lambda r: doc)(ref)
assert [x.get("자재") for x in got["안"]] == [None]
pill = got["안"][0]
times = Decimal(1) # 기계비빔타설 알약 배 1
assert pill["기준"]["계"] * times == _kept(raw)["계"]
assert got["기준"]["계"] == _kept(raw)["계"]
def test_화면_별도계상_바꾸면_다시_셈():
# 806-1 — 조합 · 서고 모두 일위대가표 부품 · 세 칸은 줄 모달(`Labor_RowModal`)
modal = open(os.path.join(SRC, "M02_MasterTemplete_Labor_RowModal.ts"), encoding="utf-8").read()
assert 'o.done(key === "별도계상");' in modal # 사용자 자재 · 로직 자재 모두 별도계상만 다시 셈
assert modal.count('o.done(key === "별도계상");') == 2
# ── ③ 원문 여럿 ──────────────────────────────────────────────────────
def test_원문_출처는_같은_책_절_모두():
src = inputs.sources(_whole().logic(MIX))
assert src == "산림품셈 12-1-2 · 12-1-3"
assert inputs.logic_inputs([MIX])["로직"][0]["출처"] == src
assert [f["number"] for f in make.source(src)["found"]] == ["12-1-2", "12-1-3"]
def test_원문_출처_번호_없거나_더할_것_없으면_그대로():
assert inputs.sources({"출처": "자체"}) == "자체"
assert inputs.sources({"출처": None}) is None
one = {
"출처": "산림품셈 3-1",
"호표": [{"출처": "건설품셈 공통 1-2-4 3"}, {"출처": "산림품셈 3-1"}],
}
assert inputs.sources(one) == "산림품셈 3-1" # 다른 책 · 같은 절은 안 붙임