- 흠: 빈 칸을 거른 뒤 차례로 읽어 규격이 빈 머리(「굴삭기(0.2m3)」 시간 …) · 손료 줄(「대형브레이카(0.7m3)」 계수 천원 …)이 한 칸씩 밀림 → 호표 통째(머리) · 조합 둘째 손료를 못 읽음 · 규격 있는 운전사 · 규격 없는 연료 줄도 같은 까닭 - 고침: 첫 글 칸부터 [명칭, 규격, 수량, 단위, 단가] 칸 자리로 읽음(단위로 갈래) - 전후: STmate 엑셀 6 파일 호표 143 — 같음 109 · 새로 읽음 34(울진대흥 · 대흥공통 16 씩 · 울진소광 2) · 바뀜 · 잃음 0 · 새로 읽은 18(같은 책 둘 뺌) 모두 같은 공사 .STC 구성과 같음 · 두 공사 87 블록 불러오기 금액 검산 같음(전후 같음) · 이 견본은 기계가 모두 M01 에 이어져 내 기계 0 대(전후 같음) · 못 이었다면 몸 = 직접 → 바로 34 - 시험: test_842_4 ⑤ 더함 · test_828_1 (100, 118) → (118, 118) — 옛 단언이 이 흠(못 읽는 머리)을 박아 둔 낡은 칸 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
158 lines
8.0 KiB
Python
158 lines
8.0 KiB
Python
# ruff: noqa: F811 — 임시 층 fixture(world)를 test_m02_labor_ref 에서 받아 씀
|
|
"""842-4 지킴 — 불러오기 내 기계 = 기계 한 대 안 여러 줄(운전사 · 연료 · 잡품 · 소모 재료)을 모두 받음(임시 storage).
|
|
|
|
① 구성 읽기 = 줄 목록(EST main — 크러셔 운전사 둘 · 펌프 준설선 넷 · 표층재생기 연료 둘 · 잡품 둘 · 대형브레이카 치즐 EA)
|
|
② 몸 = `master_machine_crew` 꼴 인력 · 잡품 줄 · 못 이음 = 비고 「확인 필요」 — main 구성 전부 버린 줄 0
|
|
③ 층에 만든 내 기계 시간당 노무비 = 줄마다 우리 노임 × 인 × 5/24 의 합(둘째 줄부터도 셈에 들어감)
|
|
④ 한 줄 구성은 전과 같은 몸(옛 칸 그대로 · 저장 때 목록으로 바뀜)
|
|
⑤ STmate 엑셀 「중기사용료」 규격 빈 칸(머리 · 손료 줄) = 칸 자리로 읽음(843-2) — 울진대흥 호표 25 모두 · .STC 구성과 같음
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from functools import lru_cache
|
|
|
|
import pytest
|
|
|
|
from M01_MasterData import M01_MasterData_Store as m01
|
|
from M02_MasterTemplete import M02_Machine_Hourly as mh
|
|
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
|
from M02_MasterTemplete import M02_MyMachine as mym
|
|
from M02_MasterTemplete import M02_San_MyMachine as mm
|
|
from resources.tester.test_821_4_import_my_machine import GEN, _line, _main
|
|
from resources.tester.test_m02_labor_ref import world # noqa: F401 — 같은 임시 층
|
|
|
|
CRUSHER = ("크러셔(이동식,발전기)", "150Ton")
|
|
DREDGE = ("펌프 준설선", "224kW")
|
|
RECYCLER = ("현장가열 표층재생기", "479kW")
|
|
BREAKER = ("대형브레이카", "0.7㎥경암")
|
|
|
|
|
|
@lru_cache(maxsize=1)
|
|
def _makes() -> dict:
|
|
return {mm.key_of(m["이름"], m["규격"]): m for m in _main()["기계구성"].values()}
|
|
|
|
|
|
def _make(want: tuple[str, str]) -> dict:
|
|
return _makes()[mm.key_of(*want)]
|
|
|
|
|
|
def _body(want: tuple[str, str]) -> dict:
|
|
return mm.body(_make(want), 0, _line(*want), "main.estx")
|
|
|
|
|
|
def _lb(code: str) -> float:
|
|
return float(m01.loaded()[1].numbers["LB"][code]["값"])
|
|
|
|
|
|
# ① 구성 읽기 ──────────────────────────────────────────────────────
|
|
def test_lines_read() -> None:
|
|
assert [j for j, _ in _make(CRUSHER)["인력줄"]] == ["건설기계운전사", "일반기계운전사"]
|
|
assert [n for _, n in _make(DREDGE)["인력줄"]] == [1.0, 2.0, 2.0, 3.0]
|
|
assert _make(RECYCLER)["연료줄"] == [["경유", 73.7], ["휘발유", 54.5]]
|
|
assert _make(RECYCLER)["잡품줄"] == [20.0, 20.0]
|
|
assert _make(BREAKER)["재료줄"] == [["치즐(0.7㎥)", 0.03, "EA"]]
|
|
assert _make(CRUSHER)["조종원직종"] == "건설기계운전사" # 첫 줄 = 옛 단일 칸 그대로
|
|
|
|
|
|
# ② 몸 ────────────────────────────────────────────────────────────
|
|
def test_body_lists() -> None:
|
|
crew = _body(CRUSHER)["값"]["인력"]
|
|
assert [r["키"] for r in crew] == ["LB:1048", "LB:1050"] and [r["인"] for r in crew] == [1, 1]
|
|
dredge = _body(DREDGE)
|
|
assert [r["키"] for r in dredge["값"]["인력"]] == ["LB:1052", "LB:1053", "LB:1054", "LB:1055"]
|
|
assert (
|
|
"선박용경유" in dredge["비고"]
|
|
and "잡품 36%" in dredge["비고"]
|
|
and mm.CHECK in dredge["비고"]
|
|
)
|
|
misc = _body(RECYCLER)["값"]["잡품"]
|
|
oil = m01.loaded()[1].numbers["MO"]["휘발유:전국평균"]["키"]
|
|
assert misc == [
|
|
{"줄": "주연료", "율": 20.0},
|
|
{"키": oil, "이름": "휘발유", "수량": 54.5, "율": 100},
|
|
{"키": oil, "이름": "휘발유 잡품", "수량": 54.5, "율": 20.0},
|
|
]
|
|
chisel = _body(BREAKER)["값"]["잡품"]
|
|
assert len(chisel) == 1 and chisel[0]["수량"] == 0.03 and chisel[0]["율"] == 100
|
|
assert m01.loaded()[1].get(chisel[0]["키"])["이름"].replace(" ", "") == "치즐(0.7㎥)"
|
|
|
|
|
|
def test_no_line_dropped() -> None:
|
|
"""main 구성 전부 — 원천 줄 수 = 받은 줄 + 비고 「확인 필요」(버린 줄 0)."""
|
|
for m in _makes().values():
|
|
b = mm.body(m, 0, _line(m["이름"], m["규격"]), "main.estx")
|
|
if b is None or b["방식"] != "바로":
|
|
continue
|
|
v, notes = (
|
|
b["값"],
|
|
[x for x in b["비고"].split(" · ") if mm.CHECK in x and x != "값 확인 필요"],
|
|
)
|
|
src = sum(len(m.get(k) or []) for k in ("인력줄", "연료줄", "재료줄"))
|
|
src += len([r for r in m.get("잡품줄") or [] if r]) # 0 % = 셈할 것 없음
|
|
crew = len(v["인력"]) if "인력" in v else int(bool(v.get("조종원")))
|
|
kept = crew + int(bool(v.get("주연료"))) + int(bool(v.get("잡재료율")))
|
|
kept += len(v.get("잡품") or [])
|
|
lost = sum(1 + ("(잡품" in x) for x in notes)
|
|
assert kept + lost == src, (m["이름"], m["규격"], kept, lost, src)
|
|
|
|
|
|
# ③ 셈 ────────────────────────────────────────────────────────────
|
|
def test_hourly_counts_every_crew(world) -> None:
|
|
me = world["personal"]
|
|
me.mkdir(parents=True, exist_ok=True)
|
|
with store.at(me):
|
|
for want, codes in (
|
|
(CRUSHER, ("1048", "1050")),
|
|
(DREDGE, ("1052", "1053", "1054", "1055")),
|
|
):
|
|
body = _body(want)
|
|
if want == DREDGE: # 선박용경유 못 이음 = 연료 없음 — 손료 · 인력만 셈
|
|
assert "주연료" not in body["값"]
|
|
key = mym.create(body)["줄"]["키"]
|
|
row = next(r for r in mym.rows() if r["키"] == key)
|
|
assert [r["키"] for r in row["값"]["인력"]] == [f"LB:{c}" for c in codes]
|
|
hour = mh.hourly(key)
|
|
assert hour["ok"], hour
|
|
want_pay = sum(_lb(c) * r["인"] for c, r in zip(codes, row["값"]["인력"])) * 5 / 24
|
|
assert abs(hour["노무비"] - want_pay) < 2, (want, hour["노무비"], want_pay)
|
|
|
|
|
|
# ④ 한 줄 구성 ─────────────────────────────────────────────────────
|
|
def test_single_line_same_as_before() -> None:
|
|
v = mm.body(_main()["기계구성"][GEN], 0, _line("발전기", "5.5kW"), "main.estx")["값"]
|
|
assert "인력" not in v and v["조종원"] == 1.0 and v["잡재료율"] == 11.0
|
|
|
|
|
|
# ⑤ STmate 엑셀 빈 규격 ─────────────────────────────────────────────
|
|
def test_stmate_excel_blank_spec() -> None:
|
|
import test_797_1a_import_flags as practice
|
|
|
|
from M02_MasterTemplete import M02_Doc_FromNative as fn
|
|
from M02_MasterTemplete import M02_San_Book as book
|
|
|
|
p = practice.FILES.get("울진대흥")
|
|
stc = next(iter(practice.golden.DOCS.rglob("2024년 간선임도사업(기번3).STC")), None)
|
|
if not p or not p.exists() or stc is None:
|
|
pytest.skip("실무 견본 없음")
|
|
got = mm.xlsx_makes(book.sheets(p.name, p.read_bytes()))
|
|
codes = sorted(k for k in got if k.startswith("X"))
|
|
assert len(codes) == 25 # 호표 25 모두(전 = 9 · 규격 빈 머리 16 못 읽음)
|
|
assert got["X00003"]["손료"] == [[2085.0, 62550000.0, False]] # 「굴삭기(0.2m3)」 규격 빈 머리
|
|
assert [x[1] for x in got["X00014"]["손료"]] == [
|
|
110926000.0,
|
|
16315000.0,
|
|
] # 둘째 손료 규격 빈 줄
|
|
native = fn.read(stc.name, stc.read_bytes())["기계구성"]
|
|
keep = ("손료", "조종원", "조종원직종", "주연료종류", "주연료", "잡재료율")
|
|
for k in codes:
|
|
a, b = got[k], native[k]
|
|
assert [[round(x, 3) for x in r[:2]] for r in a["손료"]] == [
|
|
[round(x, 3) for x in r[:2]] for r in b["손료"]
|
|
], k
|
|
assert all(a.get(f) == b.get(f) for f in keep[1:]), k
|
|
|
|
|
|
if __name__ == "__main__":
|
|
pytest.main([__file__, "-q"])
|