Files
Aislo/resources/tester/test_751_3_money_cut.py
T
eomsangdonandClaude Opus 5.5 f4be6782a5 feat(751-3): M01 · M02 셈 끝수 = 품셈 1-2-2 버림 — 줄 0.1 원 · 비목 소계 1 원 · 계 = 셋 합 · B09 와 같은 값
- 끝수 한 곳 `master_cut.py`(B09 `rounddown` 재사용) — 엔진 답에 입힘: 줄 비목마다 0.1 원 버림 · 하위 로직 줄 = 하위 끝수 계 × 수량 · 덧줄 = 버린 줄 합으로 다시 셈 · 비목 합 1 원 버림 · 중간 칸도 버린 값
- 쓰는 곳: M01 시험 계산(`check_master.calc`) · 읽는 식(`master_text.lines`) · 옛 조합 미리보기(`master_combo`) · M02 `run_line`(단가산출 · 일위대가 · 펼침) · 별도계상 뺀 기준 · 켜쌓기율 곱 뒤 · 일위대가 반영 · 반영합 · 비율 줄 · 사전조합 안 줄
- 일위대가 반영 TS 짝(`Labor_Calc.ts`) 같은 끝수 · 거울 헬퍼가 상대 경로 .ts 가져옴
- B09 = `run_line(raw=True)` 엔진 날 값(제 단가로 다시 버림 · % 덧줄 율 비 그대로) — 검증_sub3 B09 247 칸 전후 같음
- `master_text` 꼴 셋 = `common_util_money` 로 갈음(줄 금액 `fmt_line_money` · 식 안 금액 = 버린 0.1 원)
- 화면: 상세 줄 금액 · 시험 계산 줄 · 중간 돈 = `formatLineMoney` · 일위대가 반영 칸 0.1 원 · 상세 바닥 · 박스 · 인력재료경비 비목 소계 = 줄 합 1 원 버림
- 테스트1 네 보기 · 서고 목록 = 124,419(옛 124,420 / 124,419.85) · 재료 줄 11,694.6 + 23,425.6 + 1,404.8 = 소계 36,525
- 새 지킴 시험 test_751_3_money_cut
- 옛 날 금액(끝수 없음)을 대조하던 시험 24 파일 고침 — 까닭 = 금액 규칙이 줄 0.1 원 · 소계 1 원 버림으로 바뀜(고정 값 → 새 값 · 줄 합 = 계 → 줄 합 아래 비목마다 1 원 안 · 곱 비 = 끝수 폭 안) · 실무 xlsx 영월 엑셀 같음 22 → 23 · 이어 붙이는 TS 시험(704_3 · 717_2b)에 `rounddown` 같은 식

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 06:16:01 +09:00

141 lines
6.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""751-3 셈 끝수 = 품셈 1-2-2(줄 0.1 원 · 소계 1 원 미만버림) — `master_cut` 지킴 시험.
M01 시험 계산 · 읽는 식 · M02 단가산출(SK) · 일위대가 반영이 B09 끝수(`rounddown`)와 같은 값 ·
줄 합 = 소계(비목마다 ROUNDDOWN(Σ 줄, 0)) · B09 는 엔진 날 값(`raw`)을 받아 제 단가로 다시 버림.
"""
from decimal import Decimal
from B09_Estimation import B09_Estimation_Sangeun as sg
from M01_MasterData import M01_MasterData_Store as m01
from M02_MasterTemplete import M02_Labor_Doc as labor
from M02_MasterTemplete import M02_Sangeun_Calc as calc
from M02_MasterTemplete import M02_Sangeun_Doc as sd
mcut, mf = labor.mcut, labor.mf # scripts 길은 `m01` 이 둠
KEY = "GC000211" # 합판거푸집(테스트1 바탕) — 인력 · 재료 · % 덧줄 둘
COSTS = ("노무비", "재료비", "경비")
def _whole():
return m01.loaded()[1]
def _tenth(v) -> bool:
return v is None or Decimal(str(v)) == Decimal(str(v)).quantize(Decimal("0.1"))
def _sums_of_lines(lines) -> dict:
"""비목마다 ROUNDDOWN(Σ 줄, 0) — B09 `block_totals` 규칙."""
total = dict.fromkeys(COSTS, Decimal(0))
for x in lines:
for k, v in (x.get("비목") or {}).items():
total[k] += Decimal(str(v or 0))
return {k: mcut.down(v, 0) for k, v in total.items()}
def test_줄은_0점1원_소계는_1원_버림_줄합이_소계():
got = labor.run_line(_whole(), KEY, {})
base = got["기준"]
assert all(_tenth(x.get("금액")) for x in got["줄"])
assert all(Decimal(str(base[k])) == int(base[k]) for k in COSTS)
assert {k: Decimal(str(base[k])) for k in COSTS} == _sums_of_lines(got["줄"])
assert base["계"] == sum(base[k] for k in COSTS)
def test_B09_산근_끝수와_같음():
"""엔진 날 줄을 B09 규칙(줄 ROUNDDOWN 1 · % = 버린 밑 × 율 · 비목 ROUNDDOWN 0)으로 = 끝수 답."""
whole = _whole()
raw = labor.run_line(whole, KEY, {}, raw=True)
cut = labor.run_line(whole, KEY, {})
lines = []
for x in raw["줄"][: len(whole.logic(KEY)["호표"])]:
lines.append(
{k: sg._money(float(x["단가"] or 0), float(x["수량"] or 0)) for k in x["비목"]}
)
under = {k: sum(ln.get(k, 0) for ln in lines) for k in COSTS}
for x in raw["줄"][len(lines) :]: # % 덧줄 = 버린 밑 × 율 / 100 → ROUNDDOWN 1
(part,) = x["비목"]
base = under["재료비"] if "재료" in x["이름"] or "소모" in x["이름"] else under["노무비"]
lines.append({part: sg.rounddown(base * float(x["율"]) / 100, 1)})
b09 = sg.block_totals([{"labor": ln.get("노무비", 0), "material": ln.get("재료비", 0),
"expense": ln.get("경비", 0)} for ln in lines]) # fmt: skip
assert (b09["labor"], b09["material"], b09["expense"], b09["sum"]) == tuple(
float(cut["기준"][k]) for k in (*COSTS, "계")
)
assert raw["기준"]["계"] != cut["기준"]["계"] # 날 값은 버리기 전(B09 몫)
def test_화면_넷_같은_값_SK_시험계산_읽는식():
whole = _whole()
doc = sd.clean(
{"키": "SK-000000", "이름": "끝수", "단위": "㎡", "바탕": {"로직": KEY}}, None, whole
)
sk = calc.run(doc, whole)["기준"]
auto = {k: v for k, v in (m01.auto(KEY)["값"] or {}).items() if v is not None}
m1 = m01.calc(KEY, auto)
text = m01.text(KEY, auto)
assert float(sk["계"]) == m1["sums"]["계"] == float(text["계"])
assert {g["비목"]: float(g["소계"]) for g in text["groups"]} == {k: float(sk[k]) for k in COSTS}
line = next(x for g in text["groups"] for x in g["줄"] if x["이름"].startswith("형틀"))
assert line["글"].endswith(" = " + _line_text(line["금액"])) and line["글"].endswith(".9")
def _line_text(v) -> str:
from common_util.common_util_money import fmt_line_money
return fmt_line_money(Decimal(str(v)))
def test_하위_로직_줄은_하위_계를_버려_단가로():
"""기계 · 하위 로직 줄 단가 = 하위 로직 끝수 계(1 원) ·
비목마다 하위 비목 × 수량 0.1 원 버림."""
whole = _whole()
key = next(
str(r["키"])
for _, d in m01._logic_files(m01.cm.load(folder=m01.FOLDER))
for r in d.get("줄") or []
if any(i.get("종류") == "로직" for i in r.get("호표") or [])
and "결과" not in r
and not isinstance(r.get("흐름"), dict)
and labor.run_line(whole, str(r["키"]), {}).get("기준")
)
got = labor.run_line(whole, key, {})
for item, x in zip(whole.logic(key)["호표"], got["줄"]):
if item.get("종류") == "로직" and x.get("단가") is not None and x.get("수량") is not None:
assert Decimal(str(x["단가"])) == int(x["단가"])
parts = [
Decimal(str(v)) for v in x["비목"].values()
] # 비목마다 0.1 원 버림 · 금액 = 합
assert all(_tenth(v) for v in parts) and Decimal(str(x["금액"])) == sum(parts)
assert abs(Decimal(str(x["단가"])) * x["수량"] - sum(parts)) < Decimal("0.3")
def test_날_값은_엔진_그대로():
whole = _whole()
eng = mf.run(whole, KEY, {**m01.mau.values(whole, whole.logic(KEY))})
raw = labor.run_line(whole, KEY, {}, raw=True)
assert raw["기준"]["계"] == eng["계"]
def test_일위대가_반영_줄_0점1원_합_1원():
doc = labor.clean({"담은로직": [{"로직": KEY, "수량": {"손": 3.3}}]})
out = labor.calc(doc, {})
line = out["줄"][0]["반영"]
assert all(_tenth(line[k]) for k in COSTS) and line["계"] == sum(line[k] for k in COSTS)
total = out["반영합"]
assert all(float(total[k]) == int(total[k]) for k in COSTS)
assert {k: Decimal(str(total[k])) for k in COSTS} == {
k: mcut.down(Decimal(str(line[k])), 0) for k in COSTS
}
def test_별도계상_뺀_기준은_남은_줄_합_버림():
whole = _whole()
got = labor.run_line(whole, KEY, {})
marks = {str(m["차례"]): {"별도계상": "직접계산"} for m in got["재료"]}
marks[str(got["재료"][0]["차례"])] = {"별도계상": "별도계상"} # 첫 재료만 뺌
out = labor.drop_apart(got, marks)
assert len(out["줄"]) == len(got["줄"]) - 1
assert {k: Decimal(str(out["기준"][k])) for k in COSTS} == _sums_of_lines(out["줄"])