폴더마다 체크아웃 줄바꿈이 달라 메인 폴더에서 test_794_2b 셈 지문 시험이 깨짐. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
519 lines
21 KiB
Python
519 lines
21 KiB
Python
"""791-1 엔진 셈 의미 지킴 시험 — 금액번지 · 소계 단계 · 제외 · 줄 자리 · 블록 끝전 · 결과 조정 ·
|
||
특수 % · 변수 표시.
|
||
|
||
① 전후 같음 — M01 로직 전부(입력 = 자동값 + 기본값)의 날 셈 · 끝수 셈 지문 = 고치기 전 뜬 지문
|
||
(`fixtures/m01_791_1_logic_fingerprint.json` · 마스터 데이터가 바뀌면 건너뜀 → 이 파일을
|
||
바로 돌려 다시 뜸).
|
||
② 새 의미 단위 시험 · 검사.
|
||
③ 실무 손 옮김 견본 넷 — 원문 글(STmate 숨은 G 열 · EST 보이는 글)을 사람이 로직 꼴로 옮김 ·
|
||
단가 = 그 엑셀 목록표 값 · 노 · 재 · 경 · 계 = 엑셀(`helper_m02_hopyo_golden.FILES` · 649-G2).
|
||
"""
|
||
|
||
import hashlib
|
||
import importlib
|
||
import json
|
||
import sys
|
||
from decimal import Decimal
|
||
from pathlib import Path
|
||
|
||
import pytest
|
||
|
||
from M01_MasterData import M01_MasterData_Store as m01 # scripts 길을 둠
|
||
|
||
mcut = importlib.import_module("master_cut")
|
||
mf = importlib.import_module("master_formula")
|
||
ms = importlib.import_module("master_formula_sum")
|
||
|
||
ROOT = Path(__file__).resolve().parents[2]
|
||
FINGER = Path(__file__).resolve().parent / "fixtures" / "m01_791_1_logic_fingerprint.json"
|
||
COSTS = ("노무비", "재료비", "경비", "계")
|
||
D = Decimal
|
||
|
||
|
||
# ── ① 전후 같음 ────────────────────────────────────────────────────────
|
||
def _master_hash() -> str:
|
||
h = hashlib.sha256()
|
||
for path in sorted((ROOT / "resources" / "master_data").glob("*.json")):
|
||
if not path.name.startswith("_"):
|
||
raw = path.read_bytes().replace(b"\r\n", b"\n") # 줄바꿈(CRLF/LF) 무관
|
||
h.update(path.name.encode() + raw)
|
||
return h.hexdigest()[:16]
|
||
|
||
|
||
def _given(whole, row) -> dict:
|
||
import master_auto as ma
|
||
|
||
given = ma.values(whole, row)
|
||
given.update({k: v for k, v in ma.defaults(whole, row).items() if v is not None})
|
||
return given
|
||
|
||
|
||
def _print(whole, row) -> str:
|
||
key = str(row["키"])
|
||
try:
|
||
raw = mf.run(whole, key, _given(whole, row))
|
||
text = json.dumps(raw, default=str, sort_keys=True, ensure_ascii=False)
|
||
cut = mcut.engine(whole, key, raw)
|
||
text += json.dumps(cut, default=str, sort_keys=True, ensure_ascii=False)
|
||
except Exception as e: # 멈춤도 지문 — 멈춤 까닭이 바뀌어도 잡음
|
||
text = f"ERR {type(e).__name__} {e}"
|
||
return hashlib.sha256(text.encode()).hexdigest()[:16]
|
||
|
||
|
||
def fingerprints() -> dict:
|
||
files, whole = m01.loaded()
|
||
rows = [r for d in files.values() if d.get("그룹") == "로직" for r in d.get("줄", [])]
|
||
return {str(r["키"]): _print(whole, r) for r in rows}
|
||
|
||
|
||
def test_M01_로직_전부_전후_같음():
|
||
saved = json.loads(FINGER.read_text(encoding="utf-8"))
|
||
if saved["마스터"] != _master_hash():
|
||
pytest.skip("마스터 데이터 바뀜 — 지문 다시 뜸: python resources/tester/test_791_1_…py")
|
||
now = fingerprints()
|
||
assert len(now) == len(saved["결과"]) >= 1515
|
||
diff = sorted(k for k in now if now[k] != saved["결과"].get(k))
|
||
assert not diff, f"셈 결과 바뀐 로직 {len(diff)} 개: {diff[:10]}"
|
||
|
||
|
||
# ── 시험 마스터 ───────────────────────────────────────────────────────
|
||
def _master(logics: list[dict], labor: dict, mats: dict | None = None) -> mf.Master:
|
||
"""시험 전용 색인 — 인력 LB99 · 자재품목 MT99(물가자료) · 로직 GX99."""
|
||
files = {
|
||
"인력.json": {
|
||
"그룹": "인력",
|
||
"줄": [
|
||
{"키": k, "이름": n, "단위": "원/일", "값": D(v)} for k, (n, v) in labor.items()
|
||
],
|
||
},
|
||
"재료_자재품목.json": {
|
||
"그룹": "재료",
|
||
"줄": [
|
||
{"키": k, "이름": n, "단위": u, "물가자료": D(v)}
|
||
for k, (n, u, v) in (mats or {}).items()
|
||
],
|
||
},
|
||
"로직_시험.json": {"그룹": "로직", "줄": logics},
|
||
}
|
||
return mf.Master(files)
|
||
|
||
|
||
def _calc(master, key: str, given: dict | None = None) -> dict:
|
||
return mcut.engine(master, key, mf.run(master, key, given or {}))
|
||
|
||
|
||
def _money(got: dict) -> dict:
|
||
return {k: got[k] for k in COSTS}
|
||
|
||
|
||
def _line(got: dict, name: str) -> dict:
|
||
return next(x for x in got["줄"] if x["이름"] == name)
|
||
|
||
|
||
def _step(got: dict, name: str) -> dict:
|
||
return next(x for x in got["단계"] if x["이름"] == name)
|
||
|
||
|
||
def _man(key, qty, name, **more):
|
||
return {
|
||
"종류": "인력",
|
||
"요소": key,
|
||
"이름": name,
|
||
"단위": "인",
|
||
"수량": qty,
|
||
"비목": "노무비",
|
||
**more,
|
||
}
|
||
|
||
|
||
def _mat(key, qty, name, **more):
|
||
return {
|
||
"종류": "재료",
|
||
"요소": key,
|
||
"이름": name,
|
||
"단위": "식",
|
||
"수량": qty,
|
||
"비목": "재료비",
|
||
**more,
|
||
}
|
||
|
||
|
||
def _machine(qty, name, bundle):
|
||
"""기계 시간당 노 · 재 · 경 = 엑셀 중기목록표 값 그대로(M02 `값묶음` 길)."""
|
||
return {
|
||
"종류": "로직",
|
||
"요소": "로직(GX990009)",
|
||
"이름": name,
|
||
"단위": "hr",
|
||
"수량": qty,
|
||
"값묶음": dict(zip(("노무비", "재료비", "경비"), map(D, bundle))),
|
||
}
|
||
|
||
|
||
_HOUR = {
|
||
"키": "GX990009",
|
||
"이름": "시간당 기계경비(시험)",
|
||
"결과단위": "원/시간",
|
||
"입력": [],
|
||
"호표": [],
|
||
}
|
||
|
||
|
||
def _logic(key: str, **body) -> dict:
|
||
return {"키": key, "이름": key, "결과단위": "원/식", "입력": [], **body}
|
||
|
||
|
||
# ── ② 새 의미 단위 시험 ────────────────────────────────────────────────
|
||
LAB = {"LB990001": ("보통인부", 100000), "LB990002": ("특별인부", 200000)}
|
||
MAT = {"MT990001": ("자재 갑", "식", 1000), "MT990002": ("자재 을", "식", 3000)}
|
||
|
||
|
||
def _base(**body) -> mf.Master:
|
||
row = _logic(
|
||
"GX990001",
|
||
호표=[
|
||
_mat("MT990001", "1.23", "갑", 번지="A01"),
|
||
_mat("MT990002", "0.5", "을", 번지="A01"),
|
||
_man("LB990001", "0.0123", "보통", 번지="A02"),
|
||
_man("LB990002", "0.01", "특별", 번지="A02"),
|
||
],
|
||
**body,
|
||
)
|
||
return _master([row], LAB, MAT)
|
||
|
||
|
||
def test_옛_로직은_새_칸_없음():
|
||
master = _base()
|
||
for item in master.logic("GX990001")["호표"]:
|
||
del item["번지"]
|
||
got = _calc(master, "GX990001")
|
||
assert "단계" not in got and not ms.uses(master.logic("GX990001"))
|
||
assert _money(got) == {"노무비": 3230, "재료비": 2730, "경비": 0, "계": 5960}
|
||
|
||
|
||
def test_번지_율_밑():
|
||
master = _base(덧줄=[{"이름": "잡재료", "식": "번지.A01.재료비 * 10 / 100", "비목": "재료비"}])
|
||
got = _calc(master, "GX990001")
|
||
assert _line(got, "잡재료")["금액"] == D("273.0")
|
||
assert got["재료비"] == 3003
|
||
|
||
|
||
def test_단계_누계_제외_합밖():
|
||
master = _base(
|
||
덧줄=[{"이름": "공구손료", "식": "단계.'인부 소계'.노무비 * 3 / 100", "비목": "경비"}],
|
||
단계=[
|
||
{"이름": "자재 소계", "단": 1, "줄": ["갑", "을"]},
|
||
{"이름": "인부 소계", "단": 1, "줄": ["보통", "특별"]},
|
||
{"이름": "경비 소계", "단": 1, "줄": ["공구손료"], "제외": True},
|
||
{"이름": "계", "단": 2},
|
||
],
|
||
)
|
||
got = _calc(master, "GX990001")
|
||
assert _step(got, "인부 소계")["계"] == D("3230.0")
|
||
assert _line(got, "공구손료")["금액"] == D("96.9")
|
||
assert _step(got, "경비 소계") == {
|
||
"이름": "경비 소계",
|
||
"단": 1,
|
||
"노무비": 0,
|
||
"재료비": 0,
|
||
"경비": D("96.9"),
|
||
"계": D("96.9"),
|
||
"제외": True,
|
||
}
|
||
assert _step(got, "계")["계"] == D("5960.0") # 제외 단계는 위로 안 올라감
|
||
assert _money(got) == {"노무비": 3230, "재료비": 2730, "경비": 0, "계": 5960}
|
||
# 합밖 = 번지엔 쌓되 합에서 뺌
|
||
row = master.logic("GX990001")
|
||
row["호표"][1]["합밖"] = True
|
||
got = _calc(master, "GX990001")
|
||
assert got["재료비"] == 1230 and _step(got, "자재 소계")["재료비"] == D("2730.0")
|
||
|
||
|
||
def test_줄_자리_셋():
|
||
for way, want in (("반올림", 124), ("올림", 124), ("버림", 123)): # 123.5
|
||
master = _base(
|
||
덧줄=[{"이름": "덧", "식": "123.5", "비목": "경비", "자리": {"방법": way, "자리": 0}}]
|
||
)
|
||
assert _line(_calc(master, "GX990001"), "덧")["금액"] == want
|
||
master = _base(
|
||
덧줄=[
|
||
{"이름": "덧", "식": "123.456", "비목": "경비", "자리": {"방법": "반올림", "자리": 2}}
|
||
]
|
||
)
|
||
assert _line(_calc(master, "GX990001"), "덧")["금액"] == D("123.46")
|
||
|
||
|
||
def test_블록_끝전_둘():
|
||
body = {"덧줄": [{"이름": "경", "식": "0.6", "비목": "경비"}]} # 노 3230.0 · 재 2730.0 · 경 0.6
|
||
assert _calc(_base(**body), "GX990001")["계"] == 5960
|
||
got = _calc(_base(끝전="큰나머지", **body), "GX990001")
|
||
assert _money(got) == {"노무비": 3230, "재료비": 2730, "경비": 0, "계": 5960}
|
||
# 원 미만이 모여 1 원이 되면 큰 나머지 비목에 · 같으면 경 › 재 › 노
|
||
body = {
|
||
"덧줄": [
|
||
{"이름": "노", "식": "0.6", "비목": "노무비"},
|
||
{"이름": "경", "식": "0.6", "비목": "경비"},
|
||
]
|
||
}
|
||
assert _calc(_base(**body), "GX990001")["계"] == 5960
|
||
got = _calc(_base(끝전="큰나머지", **body), "GX990001")
|
||
assert _money(got) == {"노무비": 3230, "재료비": 2730, "경비": 1, "계": 5961}
|
||
|
||
|
||
def test_결과금액_조정():
|
||
got = _calc(_base(조정={"옮김": "경비"}), "GX990001")
|
||
assert _money(got) == {"노무비": 0, "재료비": 0, "경비": 5960, "계": 5960}
|
||
got = _calc(_base(조정={"배율": {"노무비": D("0.5")}}), "GX990001")
|
||
assert _money(got) == {"노무비": 1615, "재료비": 2730, "경비": 0, "계": 4345}
|
||
assert mf.run(_base(조정={"옮김": "재료비"}), "GX990001", {})["재료비"] == D("5960.0")
|
||
|
||
|
||
def test_특수_퍼센트_누계와_앞_소계():
|
||
master = _base(
|
||
덧줄=[
|
||
{"이름": "L%", "식": "노무비 * 2 / 100", "비목": "경비"}, # 처음부터 노무비 누계
|
||
{"이름": "T%", "식": "단계.'소계'.계 * 0.8", "비목": "경비"}, # 바로 앞 소계 × 수량
|
||
],
|
||
단계=[{"이름": "소계", "단": 1, "줄": ["갑", "을"]}],
|
||
)
|
||
got = _calc(master, "GX990001")
|
||
assert _line(got, "L%")["금액"] == D("64.6")
|
||
assert _line(got, "T%")["금액"] == D("2184.0")
|
||
|
||
|
||
def test_변수_표시_반올림():
|
||
row = _logic(
|
||
"GX990002",
|
||
중간=[{"이름": "f", "식": "1 / 1.3", "표시": 2}, {"이름": "g", "식": "1 / 1.3"}],
|
||
결과={"식": "f * 100"},
|
||
)
|
||
got = mf.run(_master([row], LAB), "GX990002", {})
|
||
assert got["결과"] == D("77.00") and got["중간"]["g"] != D("0.77")
|
||
|
||
|
||
def test_검사():
|
||
def problems(**body) -> list[str]:
|
||
master = _base(**body)
|
||
return mf.check_logic(master, master.logic("GX990001"))[0]
|
||
|
||
assert problems(덧줄=[{"이름": "덧", "식": "번지.A01.재료비 * 1", "비목": "경비"}]) == []
|
||
assert any(
|
||
"없는 번지" in x
|
||
for x in problems(덧줄=[{"이름": "덧", "식": "번지.B09.계", "비목": "경비"}])
|
||
)
|
||
order = problems(
|
||
덧줄=[{"이름": "덧", "식": "단계.'소'.계", "비목": "경비"}],
|
||
단계=[{"이름": "소", "단": 1, "줄": ["갑", "덧"]}],
|
||
)
|
||
assert any("단계 순서" in x for x in order)
|
||
assert any(
|
||
"자리" in x
|
||
for x in problems(
|
||
덧줄=[{"이름": "덧", "식": "1", "비목": "경비", "자리": {"방법": "버림", "자리": 4}}]
|
||
)
|
||
)
|
||
assert any("끝전" in x for x in problems(끝전="합버림"))
|
||
assert any("단 「6」" in x for x in problems(단계=[{"이름": "소", "단": 6, "줄": ["갑"]}]))
|
||
assert any("없는 줄" in x for x in problems(단계=[{"이름": "소", "단": 1, "줄": ["없음"]}]))
|
||
assert any("조정 옮김" in x for x in problems(조정={"옮김": "계"}))
|
||
assert any(
|
||
"이 단계" in x
|
||
for x in problems(
|
||
단계=[{"이름": "가", "단": 1, "줄": ["갑"]}, {"이름": "나", "단": 1, "줄": ["갑"]}]
|
||
)
|
||
)
|
||
|
||
|
||
# ── ③ 실무 손 옮김 견본 넷 ─────────────────────────────────────────────
|
||
def 봉화():
|
||
"""봉화 단가산출근거 2909~2943 행 — `&M00081& * 1.03 * 0.327 :=A01` · `&PA01M:M& * 11'%'` ·
|
||
`&PA02L:S& * 1'%'` · `(=)` · `(==)` · 총계 45,652 · 34,119 · 11,192 · 341."""
|
||
row = _logic(
|
||
"GX990101",
|
||
호표=[
|
||
_mat("MT990101", "1.03 * 0.327", "합판(내수)", 번지="A01"),
|
||
_mat("MT990102", "0.038 * 0.327", "각재", 번지="A01"),
|
||
_man("LB990101", "5 / 50", "형틀목공", 번지="A02"),
|
||
_man("LB990102", "2 / 50", "보통인부", 번지="A02"),
|
||
],
|
||
덧줄=[
|
||
{"이름": "소모자재(박리재 등)", "식": "번지.A01.재료비 * 11 / 100", "비목": "재료비"},
|
||
{
|
||
"이름": "공구손료 및 경장비 기계경비",
|
||
"식": "번지.A02.노무비 * 1 / 100",
|
||
"비목": "경비",
|
||
},
|
||
],
|
||
단계=[
|
||
{"이름": "자재비 소계", "단": 1, "줄": ["합판(내수)", "각재", "소모자재(박리재 등)"]},
|
||
{
|
||
"이름": "설치 소계",
|
||
"단": 1,
|
||
"줄": ["형틀목공", "보통인부", "공구손료 및 경장비 기계경비"],
|
||
},
|
||
{"이름": "계", "단": 2},
|
||
],
|
||
)
|
||
master = _master(
|
||
[row],
|
||
{"LB990101": ("형틀목공", 274978), "LB990102": ("보통인부", 165545)},
|
||
{"MT990101": ("합판(내수)", "㎡", 11468), "MT990102": ("각재", "㎥", 500661)},
|
||
)
|
||
return master, row, {"노무비": 34119, "재료비": 11192, "경비": 341, "계": 45652}
|
||
|
||
|
||
def test_봉화_D00102_합판거푸집_STmate_번지_율():
|
||
master, row, want = 봉화()
|
||
assert mf.check_logic(master, row)[0] == []
|
||
got = _calc(master, "GX990101")
|
||
assert _money(got) == want
|
||
assert [x["금액"] for x in got["줄"]] == [
|
||
D(x) for x in ("3862.5", "6221.2", "27497.8", "6621.8", "1109.2", "341.1")
|
||
]
|
||
assert [x["계"] for x in got["단계"]] == [D("11192.9"), D("34460.7"), D("45653.6")]
|
||
|
||
|
||
def 소광():
|
||
"""울진소광 32 호표(D01489) — `'공구손료 :'&S01045& * 2'%'`(S01045 단위 L% = 처음부터
|
||
노무비 누계) · `L = Q/10 =` 표시 2 · 계 줄 420,734 · 182,223 · 230,359 · 8,152
|
||
(계약단가 87.745 % 는 B09 몫)."""
|
||
row = _logic(
|
||
"GX990102",
|
||
중간=[
|
||
{"이름": "N", "식": "40"},
|
||
{"이름": "Q", "식": "200"},
|
||
{"이름": "L", "식": "Q / 10", "표시": 2},
|
||
{"이름": "Q1", "식": "360"},
|
||
{"이름": "A", "식": "5.2"},
|
||
],
|
||
호표=[
|
||
_mat("MT990103", "1", "낙석방지책"),
|
||
_man("LB990103", "1 / N", "용접공"),
|
||
_man("LB990104", "3 / N", "특별인부(지주)"),
|
||
_man("LB990105", "2 / N", "보통인부(지주)"),
|
||
_machine("1 / N * 8", "트럭탑재형 크레인 10톤", (49479, 16797, 22539)),
|
||
_man("LB990104", "4 / Q * L", "특별인부(와이어)"),
|
||
_man("LB990105", "2 / Q * L", "보통인부(와이어)"),
|
||
_man("LB990104", "4 / Q1 * A", "특별인부(철망)"),
|
||
_man("LB990105", "2 / Q1 * A", "보통인부(철망)"),
|
||
],
|
||
덧줄=[{"이름": "공구손료", "식": "노무비 * 2 / 100", "비목": "경비"}],
|
||
단계=[
|
||
{"이름": "재료비 계", "단": 2, "줄": ["낙석방지책"]},
|
||
{"이름": "인건비 소계", "단": 1, "줄": ["용접공", "특별인부(지주)", "보통인부(지주)"]},
|
||
{"이름": "크레인 소계", "단": 1, "줄": ["트럭탑재형 크레인 10톤"]},
|
||
{"이름": "지주 계", "단": 2},
|
||
{"이름": "와이어 계", "단": 2, "줄": ["특별인부(와이어)", "보통인부(와이어)"]},
|
||
{"이름": "철망 계", "단": 2, "줄": ["특별인부(철망)", "보통인부(철망)"]},
|
||
{"이름": "공구손료 계", "단": 2, "줄": ["공구손료"]},
|
||
{"이름": "합계", "단": 3},
|
||
],
|
||
)
|
||
master = _master(
|
||
[row, _HOUR],
|
||
{
|
||
"LB990103": ("용접공", 278326),
|
||
"LB990104": ("특별인부", 221506),
|
||
"LB990105": ("보통인부", 169804),
|
||
},
|
||
{"MT990103": ("낙석방지책", "경간", 227000)},
|
||
)
|
||
return master, row, {"노무비": 182223, "재료비": 230359, "경비": 8152, "계": 420734}
|
||
|
||
|
||
def test_울진소광_D01489_낙석방지책_STmate_L퍼센트():
|
||
master, row, want = 소광()
|
||
assert mf.check_logic(master, row)[0] == []
|
||
got = _calc(master, "GX990102")
|
||
assert _money(got) == want
|
||
assert _line(got, "공구손료")["금액"] == D("3644.4")
|
||
assert _step(got, "지주 계")["계"] == D("49824.2")
|
||
assert _step(got, "합계")["계"] == D("420735.3")
|
||
|
||
|
||
def 영월():
|
||
"""동부청영월 단가산출근거 54~103 행(D00003) — `f = 1/1.3 =` · `E=…=` ·
|
||
`Q = 3600*q*K*f*E/Cm =`(표시 2) · `&X00205L/M/S& / Q * 0.9` · 계 원 버림 + 큰 나머지
|
||
5,471 · 4,764 · 310 · 397."""
|
||
row = _logic(
|
||
"GX990103",
|
||
중간=[
|
||
{"이름": "q", "식": "0.7"},
|
||
{"이름": "f", "식": "1 / 1.3", "표시": 2},
|
||
{"이름": "K", "식": "0.9"},
|
||
{"이름": "Cm", "식": "20"},
|
||
{"이름": "E", "식": "(0.7 + 0.6) / 2 - 0.05", "표시": 2},
|
||
{"이름": "Q", "식": "3600 * q * K * f * E / Cm", "표시": 2},
|
||
],
|
||
호표=[
|
||
_man("LB990106", "(0.2 + 0.26) / 2 * 0.1", "인부"),
|
||
_machine("1 / Q * 0.9", "유압식 백호우 0.7㎥", (55700, 18015, 23128)),
|
||
],
|
||
단계=[
|
||
{"이름": "인력 소계", "단": 1, "줄": ["인부"]},
|
||
{"이름": "기계 소계", "단": 1, "줄": ["유압식 백호우 0.7㎥"]},
|
||
{"이름": "계", "단": 2},
|
||
],
|
||
끝전="큰나머지",
|
||
)
|
||
master = _master([row, _HOUR], {"LB990106": ("보통인부", 165545)})
|
||
return master, row, {"노무비": 4764, "재료비": 310, "경비": 397, "계": 5471}
|
||
|
||
|
||
def test_동부청영월_D00003_터파기_STmate_큰나머지_끝전():
|
||
master, row, want = 영월()
|
||
assert mf.check_logic(master, row)[0] == []
|
||
got = _calc(master, "GX990103")
|
||
assert got["중간"]["Q"] == D("52.39")
|
||
assert _money(got) == want
|
||
assert _step(got, "계")["계"] == D("5471.0")
|
||
row["끝전"] = "비목버림" # 비목 원 버림이면 1 원 모자람
|
||
assert _calc(master, "GX990103")["계"] == 5470
|
||
|
||
|
||
def 임도():
|
||
"""북부청임도 단가산출 18 번(문양거푸집) — `5,786.1 × 2% = 116`(그 줄만 반올림 0) ·
|
||
`인부소계 : 24,467.3` · 노무비 24,467 · 재료비 5,902 · 계 30,369."""
|
||
row = _logic(
|
||
"GX990104",
|
||
호표=[
|
||
_mat("MT990104", "1 * (1 / 0.72)", "문양거푸집(ABS코팅폼)"),
|
||
_man("LB990107", "0.07", "형틀목공"),
|
||
_man("LB990108", "0.03", "보통인부"),
|
||
],
|
||
덧줄=[
|
||
{
|
||
"이름": "잡재료비",
|
||
"식": "재료비 * 2 / 100",
|
||
"비목": "재료비",
|
||
"자리": {"방법": "반올림", "자리": 0},
|
||
}
|
||
],
|
||
단계=[{"이름": "인부소계", "단": 1, "줄": ["형틀목공", "보통인부"]}],
|
||
)
|
||
master = _master(
|
||
[row],
|
||
{"LB990107": ("형틀목공", 275790), "LB990108": ("보통인부", 172068)},
|
||
{"MT990104": ("문양거푸집(ABS코팅폼)", "㎡", 4166)},
|
||
)
|
||
return master, row, {"노무비": 24467, "재료비": 5902, "경비": 0, "계": 30369}
|
||
|
||
|
||
def test_북부청임도_18_문양거푸집_EST_줄_자리():
|
||
master, row, want = 임도()
|
||
assert mf.check_logic(master, row)[0] == []
|
||
got = _calc(master, "GX990104")
|
||
assert _line(got, "잡재료비")["금액"] == 116
|
||
assert _step(got, "인부소계")["계"] == D("24467.3")
|
||
assert _money(got) == want
|
||
del row["덧줄"][0]["자리"] # 기본 0.1 버림이면 엑셀과 1 원 다름
|
||
assert _calc(master, "GX990104")["계"] == 30368
|
||
|
||
|
||
if __name__ == "__main__": # 지문 다시 뜸 — 마스터 데이터가 바뀐 뒤 · 엔진 고치기 전 커밋에서만
|
||
data = {"마스터": _master_hash(), "결과": fingerprints()}
|
||
FINGER.write_text(
|
||
json.dumps(data, ensure_ascii=False, indent=1, sort_keys=True) + "\n", encoding="utf-8"
|
||
)
|
||
print(len(data["결과"]), "개 지문", FINGER, file=sys.stderr)
|