- 조합 상세 · 카드 · 서고 모두 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>
156 lines
8.0 KiB
Python
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" # 다른 책 · 같은 절은 안 붙임
|