- M02_Doc_Match_Note.rate_pairs → (다른 항, 참고) · 「율 값」 다른 항 폐지(EXPLAINED 에서도 뺌) · 띠 참고 요약에 「율 값 다름 N 줄」 - 시험: test_812_2 율 값 = 참고로 고침 · test_812_4 새(리퍼 105 · 275 확정 + 참고 · 율 대상 다름 = 다른 항 · 바꾸기 뒤 M01 율 · 되돌리기) - 전수 2,220: 확정 152 → 163 · 근접 588 → 577 · 없음 1,480 · 확정 다시 셈 다름 0
181 lines
9.7 KiB
Python
181 lines
9.7 KiB
Python
"""812-2 지킴 — 연결 찾기 확정 = 로직(요구 712) · 값 차이 = 참고(`M02_Doc_Match_Note`).
|
||
|
||
(a) 단가만 다름 = 확정 + 참고 「단가 차이」(원파일 기계경비 묶음 ↔ 우리 기계 = 이름 종류 같을 때) ·
|
||
끝수만 다름 = 확정 + 참고 「끝수」
|
||
(b) 로직 차이 = 다른 항 — 율 대상 · 기계 규격 · 노임 종류(「자원」) · 산출 단가 · 비목
|
||
(c) 율 = 비율 — 거창 합판거푸집(블록 41): 가져온 「주자재소계 × 9 %」 = M01 소모자재 9 % 짝 · 율_2 1 % 재료비 ↔
|
||
공구손료 1 % 경비 = 「비목」 · 대전 합판 단가 = 참고
|
||
(d) 요구 704 배수관부설 D=800 ↔ GF000179 = 여전히 근접(단위 환산 × 6 · 기계 규격 · 율 줄)
|
||
(e) 전수(S8122_ALL=1) — 2,220 블록 · 확정 다시 셈 같음 · 근접 까닭에 단가 차이 · 끝수 없음 · 확정 참고 있음
|
||
원파일 없으면 (c) · (d) · (e) skip(다른 PC).
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import json
|
||
import os
|
||
import subprocess
|
||
import sys
|
||
from decimal import Decimal as D
|
||
from pathlib import Path
|
||
|
||
import pytest
|
||
|
||
HERE = Path(__file__).resolve().parent
|
||
ROOT = HERE.parents[1]
|
||
for p in (ROOT, HERE):
|
||
if str(p) not in sys.path:
|
||
sys.path.insert(0, str(p))
|
||
|
||
from M02_MasterTemplete import M02_Doc_Match as dm # noqa: E402
|
||
|
||
TOT = {"노무비": 9, "재료비": 0, "경비": 0, "계": 9}
|
||
|
||
|
||
def _line(name, kind, qty, price, cost="노무비", code=""):
|
||
q, p = D(qty), D(price)
|
||
return {"번": 0, "이름": name, "갈래": kind, "코드": code, "수량": dm._key(q),
|
||
"단가": dm._key(p), "비목": {cost: dm._key(q * p)}, "금액": dm._key(q * p), "합밖": False} # fmt: skip
|
||
|
||
|
||
def _rate(name, rate, target=(("노임", "노무비"),), cost="경비"):
|
||
return {"번": 0, "이름": name, "갈래": "율", "코드": "", "수량": None, "단가": None,
|
||
"비목": {cost: D(1)}, "금액": D(1), "합밖": False, "율": D(rate), "대상": frozenset(target)} # fmt: skip
|
||
|
||
|
||
A = [_line("인부", "노임", "0.04", "100", code="LB1"), _line("공", "노임", "0.5", "10", code="LB2")]
|
||
|
||
|
||
# ── (a) 값 차이 = 참고 ───────────────────────────────────────────────────
|
||
def test_price_only_is_sure_with_note() -> None:
|
||
b = [_line("인부", "노임", "0.04", "120", code="LB1"), A[1]]
|
||
res = dm.verdict(A, b, TOT, {**TOT, "노무비": 10, "계": 10})
|
||
assert res["판정"] == dm.SURE and not res["다른"] and res["같음"] == 2
|
||
assert [d["항"] for d in res["참고"]] == ["단가 차이"]
|
||
mach = [_line("굴삭기", "기계", "0.1", "500", "경비", "0201-0070")]
|
||
other = [_line("굴삭기", "기계", "0.1", "530", "경비", "0201-0070")]
|
||
assert dm.verdict(mach, other, TOT, TOT)["판정"] == dm.SURE # 기계 시간당 단가 = 참고
|
||
file = [
|
||
_line("머캐덤로울러(자주식)", "산출", "0.1", "530", "경비")
|
||
] # 원파일 기계경비 묶음(코드 없음)
|
||
ours = [_line("탠덤롤러", "기계", "0.1", "500", "경비", "1206-0008")]
|
||
ours.append(_line("머캐덤롤러 10∼12ton", "기계", "0.1", "500", "경비", "1106-0012"))
|
||
res = dm.verdict(file, ours, TOT, TOT) # 이름 종류 같은 기계와 짝 · 단가 = 참고
|
||
assert (
|
||
not [d for d in res["다른"] if d["항"] != "줄 없음"] and res["참고"][0]["항"] == "단가 차이"
|
||
)
|
||
run = [{**_line("드릴", "산출", "0.1", "530", "경비"), "비목": {"노무비": D(1), "경비": D(2)}}]
|
||
res = dm.verdict(run, [_line("드릴", "기계", "0.1", "500", "경비", "0901-0010")], TOT, TOT)
|
||
assert res["판정"] == dm.SURE # 기계경비 구성(운전 노무비 포함) 차이 = 시간당 단가 = 참고
|
||
none = [{**_line("모래", "재료", "1", "0", "재료비"), "비목": {}}]
|
||
assert (
|
||
dm.verdict([_line("모래", "재료", "1", "9", "재료비")], none, TOT, TOT)["판정"] == dm.SURE
|
||
)
|
||
res = dm.verdict(file, ours[:1], TOT, TOT)
|
||
assert [d["항"] for d in res["다른"]] == ["기계 규격"]
|
||
|
||
|
||
def test_rounding_only_is_sure_with_note() -> None:
|
||
res = dm.verdict(A, list(A), TOT, {**TOT, "노무비": 10, "계": 10})
|
||
assert res["판정"] == dm.SURE and [d["항"] for d in res["참고"]] == ["끝수"]
|
||
|
||
|
||
# ── (b) 로직 차이 = 다른 항 ──────────────────────────────────────────────
|
||
def test_rate_value_is_note_structure_is_logic() -> None:
|
||
res = dm.verdict([*A, _rate("공구손료", "0.02")], [*A, _rate("공구손료", "0.03")], TOT, TOT)
|
||
assert not res["다른"] and res["판정"] == dm.SURE
|
||
assert [d["항"] for d in res["참고"]] == ["율 값"] # 율 값(%) 다름 = 참고(812-4) · 로직 같음
|
||
assert "2 % ↔" in res["참고"][0]["글"] and "3 %" in res["참고"][0]["글"]
|
||
same = dm.verdict([*A, _rate("율", "0.02")], [*A, _rate("공구손료", "0.02")], TOT, TOT)
|
||
assert same["판정"] == dm.SURE # 율 · 대상 · 비목 같음 = 이름 달라도 같은 율 줄
|
||
tgt = dm.verdict(
|
||
[*A, _rate("율", "0.02", (("재료", "재료비"),))], [*A, _rate("율", "0.02")], TOT, TOT
|
||
)
|
||
assert [d["항"] for d in tgt["다른"]] == ["율 대상"]
|
||
cost = dm.verdict([*A, _rate("율", "0.02", cost="재료비")], [*A, _rate("율", "0.02")], TOT, TOT)
|
||
assert [d["항"] for d in cost["다른"]] == ["비목"]
|
||
|
||
|
||
def test_machine_labor_kind_differs_blocks() -> None:
|
||
a = [*A, _line("굴삭기", "기계", "0.1", "500", "경비", "0201-0070")]
|
||
b = [*A, _line("굴삭기", "기계", "0.1", "500", "경비", "0201-0080")]
|
||
res = dm.verdict(a, b, TOT, TOT)
|
||
assert [d["항"] for d in res["다른"]] == ["기계 규격"] and res["판정"] == dm.NEAR
|
||
c = [_line("특별인부", "노임", "0.04", "120", code="LB9"), A[1]]
|
||
res = dm.verdict(A, c, TOT, TOT)
|
||
assert [d["항"] for d in res["다른"]] == ["자원"] and res["판정"] != dm.SURE
|
||
sub = [_line("모르타르", "산출", "0.1", "500", "재료비")]
|
||
res = dm.verdict(sub, [_line("모르타르", "산출", "0.1", "520", "재료비")], TOT, TOT)
|
||
assert [d["항"] for d in res["다른"]] == ["산출 단가"] # 안쪽 산출 = 로직 결과
|
||
|
||
|
||
# ── (c) · (d) 원파일 블록 ────────────────────────────────────────────────
|
||
def _judge(no: int) -> dict:
|
||
t4 = pytest.importorskip("test_810_4_match")
|
||
try:
|
||
return t4.judge(no)
|
||
except FileNotFoundError:
|
||
pytest.skip("원파일 없음")
|
||
|
||
|
||
def test_formwork_rate_ratio_pairs() -> None:
|
||
got = _judge(41) # 합판거푸집 4회 ↔ GC000211
|
||
assert got["판정"] == dm.NEAR and got["키"] == "GC000211"
|
||
assert [d["항"] for d in got["다른"]] == ["비목"] and "1 %" in got["다른"][0]["글"]
|
||
assert [d["항"] for d in got["참고"]] == ["단가 차이"]
|
||
|
||
|
||
def test_req704_still_near() -> None:
|
||
got = _judge(19) # 배수관부설 D=800 ↔ GF000179
|
||
kinds = [d["항"] for d in got["다른"]]
|
||
assert got["판정"] == dm.NEAR and got["키"] == "GF000179"
|
||
assert kinds.count("단위 환산") == 2 and "기계 규격" in kinds and "율 줄" in kinds
|
||
|
||
|
||
# ── (e) 전수 ─────────────────────────────────────────────────────────────
|
||
@pytest.mark.skipif(os.environ.get("S8122_ALL") != "1", reason="전수 = S8122_ALL=1")
|
||
def test_every_block_logic() -> None:
|
||
out = subprocess.run(
|
||
[sys.executable, str(HERE / "helper_810_4_sweep.py")], capture_output=True, text=True
|
||
)
|
||
assert out.returncode == 0, out.stderr[-2000:]
|
||
got = json.loads(out.stdout.strip().splitlines()[-1])
|
||
assert got["블록"] == 2220 and got["오류"] == 0 and got["확정다시셈다름"] == 0
|
||
assert not {"단가 차이", "끝수"} & set(got["근접항"])
|
||
assert got["확정참고블록"] >= 1 and got["판정"][dm.SURE] >= 98
|
||
|
||
|
||
def test_rate_target_scope() -> None:
|
||
"""율 대상 — 줄 기호 = 그 줄 · 비목 이름 = 그 비목 몫(기계 운전 노무비 포함) · 번지 합 = 핵심 줄 전부."""
|
||
from M02_MasterTemplete import M02_Doc_Match_Note as note
|
||
|
||
def run(rate_text: str) -> dict:
|
||
money = [
|
||
{"이름": "인부", "수량": "1", "단가": "100", "기호": "C_인부"},
|
||
{"이름": "굴삭기", "수량": "1", "단가": "300"},
|
||
{"이름": "율", "단가": rate_text},
|
||
]
|
||
rows = [
|
||
{"수량": D(1), "단가": D(100), "금액": D(100)},
|
||
{"수량": D(1), "단가": D(300), "금액": D(300)},
|
||
{"수량": D(1), "단가": D(0), "금액": D(0)},
|
||
]
|
||
facts = [
|
||
{"번": 0, "갈래": "노임", "비목": {"노무비": D(100)}, "합밖": False},
|
||
{"번": 1, "갈래": "산출", "비목": {"노무비": D(100), "경비": D(200)}, "합밖": False},
|
||
{"번": 2, "갈래": "율", "비목": {"경비": D(4)}, "합밖": False},
|
||
]
|
||
doc = {"변수": [], "식": [], "금액": money}
|
||
rows[2]["단가"] = {"C_인부 × 4%": D(4), "노무비 × 2%": D(4), "번지.A01.계 × 1%": D(4)}[
|
||
rate_text
|
||
]
|
||
note.rates(doc, {"줄": rows}, facts)
|
||
return facts[2]
|
||
|
||
one = run("C_인부 × 4%")
|
||
assert one["율"] == D("0.04") and one["대상"] == {("노임", "노무비")}
|
||
labor = run("노무비 × 2%") # 노무비 200(인부 100 + 굴삭기 운전 100)
|
||
assert labor["율"] == D("0.02") and labor["대상"] == {("노임", "노무비"), ("기계", "노무비")}
|
||
whole = run("번지.A01.계 × 1%") # 400 × 1 %
|
||
assert whole["율"] == D("0.01") and ("기계", "경비") in whole["대상"]
|