- master_sheet_line 장비(기계경비) 줄에 「펼침」 = {키, 이름, 단위, 입력, 줄[비목 · 이름 · 글 · 금액], 소계, 계} · 못 셈 = 까닭
- 셈 결과(master_text.lines)를 그대로 실음 — 조종원 환산 같은 규칙을 여기 두지 않음
- annotate 가 files 를 받음(펼침 계산서) · M01 /text · M02 /logic-lib/sheet 같은 KEYS
- 시험 test_714_1_sheet_line 펼침 · 두 길 같은 수(JSON 수 규칙)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
76 lines
3.3 KiB
Python
76 lines
3.3 KiB
Python
"""714-1 지킴 — 계산서 줄 덧칸(쓰는 입력 · 묶음 · 기준단위 · 환산 근거) · M01 `/text` 와
|
|
M02 `/logic-lib/sheet` 가 같은 줄 꼴(`master_sheet_line`)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
from M01_MasterData import M01_MasterData_Store as store # noqa: E402
|
|
from M01_MasterData import M01_MasterData_Store_Sheet as sheet # noqa: E402
|
|
from M02_MasterTemplete import M02_Logic_Standard as standard # noqa: E402
|
|
|
|
msl = sheet.msl
|
|
num = standard.labor_inputs._num # Decimal → JSON 수(두 길 같은 규칙)
|
|
|
|
|
|
def _text(key: str) -> dict:
|
|
auto = {k: v for k, v in store.auto(key)["값"].items() if v is not None}
|
|
return sheet.text(key, auto)
|
|
|
|
|
|
def _lines(got: dict, groups: str = "groups") -> list[dict]:
|
|
return [line for g in got[groups] for line in g["줄"]]
|
|
|
|
|
|
def test_line_keys_and_bundle():
|
|
got = _text("GF000103") # 구조물터파기 — 인력 + 굴착기 기계경비 줄
|
|
assert got["ok"] and got["기준단위"] == "㎥"
|
|
lines = _lines(got)
|
|
assert all({"쓰는입력", "묶음", "기준단위", "환산근거"} <= set(line) for line in lines)
|
|
machine = [line for line in lines if line.get("장비")]
|
|
assert machine and all(line["묶음"] == "경비" for line in machine)
|
|
assert {"지역", "보정작업"} <= set(machine[0]["쓰는입력"])
|
|
assert any(line["묶음"] == "인력" for line in lines)
|
|
|
|
|
|
def test_machine_spread():
|
|
"""714-1b — 장비 줄 「펼침」 = 부른 기계경비 로직 계산서(조종원 · 연료 · 손료) · 시간당 계."""
|
|
lines = _lines(_text("GF000103"))
|
|
spread = next(line for line in lines if line.get("장비"))["펼침"]
|
|
assert spread["키"].startswith("GC0002") and spread["단위"] == "시간"
|
|
names = " ".join(one["이름"] for one in spread["줄"])
|
|
assert "조종원" in names and "손료" in names
|
|
sums = [v for v in spread["소계"].values() if v is not None]
|
|
assert spread["계"] == sum(sums) and spread["계"] > 0
|
|
assert not any(line.get("펼침") for line in lines if not line.get("장비"))
|
|
|
|
|
|
def test_uses_follow_middle_values():
|
|
"""중간값을 거슬러 입력까지 — 돌쌓기 석공 품은 높이 · 쌓기 · 돌 입력을 씀."""
|
|
lines = _lines(_text("GF000219"))
|
|
assert {"높이", "쌓기", "돌"} <= set(lines[0]["쓰는입력"])
|
|
|
|
|
|
def test_basis_note_tenfold():
|
|
"""표 10㎡당 → 결과 원/㎡ 「÷10」 · 결과도 원/10㎡ 면 근거 없음."""
|
|
one = _lines(_text("GC000037"))
|
|
assert one[0]["기준단위"] == "10㎡" and one[0]["환산근거"] == "10㎡당 → 1㎡당 ÷10"
|
|
ten = _text("GF000124")
|
|
assert ten["기준단위"] == "10㎡" and all(not x["환산근거"] for x in _lines(ten))
|
|
|
|
|
|
def test_two_routes_same_shape():
|
|
"""M01 `/text` 와 M02 계산서 — 줄 차례 · 덧칸 값이 같음(금액 끝수만 다를 수 있음)."""
|
|
for key in ("GF000169", "GF000103", "GF000219"):
|
|
m01 = _lines(_text(key))
|
|
m02 = _lines(standard.sheet(key, {}), "묶음")
|
|
assert len(m01) == len(m02)
|
|
for a, b in zip(m01, m02):
|
|
assert set(b) == {"이름", "글", "금액", *msl.KEYS}
|
|
assert a["이름"] == b["이름"]
|
|
assert num([a.get(k) for k in msl.KEYS]) == [b.get(k) for k in msl.KEYS]
|