Files
Aislo/resources/tester/test_145_5_b09_summary.py
T
eomsangdonandClaude Opus 5.5 463214d921 feat(B09): 집계표 넷 — 재료비 · 노무비 · 경비 수량금액 · 중기시간금액(서버 역전개 · 145-5)
- 역전개(서버 단독) = 설계내역서 수량 × 호표 구성(겹 호표 끝까지 · 깊이 한도) → 자원 총수량 · 내역 자재 줄 그대로
- 끝수 = 엑셀 자리 — 총수량 ROUND 2 · 금액 ROUND(수량 × 단가, 0) · 중기 비목마다 ROUND · 단가 없으면 금액 빈칸
- 호표 구성 = hopyo_lines 자리(145-1 · 2 가 들어오면 채움) · GET 응답 summary · 화면 탭 넷 + 목록표 이동
- 시험 = test_145_5_b09_summary(엑셀 중기 42.95 시간 5,411,700 · 건축목공 2,222,201 · 각재 280,370 · 못 5,321 · 구역화물 678,345)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015KYZ2HWWdUwZWYnn3tQz7X
2026-10-09 15:16:58 +09:00

98 lines
4.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.
"""145-5 B09 집계표 넷 — 역전개(겹 호표) · 엑셀 끝수 · 검산 대조(서버 단독 · 거울 없음).
견본 = 기번3 공통 엑셀 집계표:
중기시간금액집계표 4줄 불도우저19ton 42.95 시간 → 2,392,315 · 1,584,340 · 1,435,045 = 5,411,700
노무비수량금액집계표 4줄 건축목공 8.29 인 × 268,058 = 2,222,201
재료비수량금액집계표 각재 0.56 × 500,661 = 280,370 · 못 3.54 × 1,503 = 5,321
경비수량금액집계표 구역화물자동차 11.03 회 × 61,500 = 678,345
"""
import asyncio
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from B09_Estimation import B09_Estimation_Router as router_mod # noqa: E402
from B09_Estimation.B09_Estimation_Summary import compute_summary # noqa: E402
SHEETS = {
"labor": [{"key": "L1", "name": "건축목공", "spec": "", "unit": "인", "price": 268058}],
"material": [
{"key": "M3", "name": "각재", "spec": "외송", "unit": "M3", "price": 500661},
{"key": "M4", "name": "못", "spec": "N 50", "unit": "KG", "price": 1503},
],
"expense": [
{"key": "E16", "name": "구역화물자동차", "spec": "5톤", "unit": "회", "price": 61500}
],
"machine": [
{
"key": "X1",
"name": "불도우저19ton",
"spec": "(토사)",
"unit": "시간",
"sum": 126000,
"labor": 55700,
"material": 36888,
"expense": 33412,
},
], # fmt: skip
"bill": {
"rows": [
{"kind": "title", "key": "sec:1", "qty": None},
{"kind": "item", "key": "a", "qty": 10, "ref": {"tab": "sg_list", "key": "A"}},
{"kind": "item", "key": "m", "qty": 0.56, "ref": {"tab": "material", "key": "M3"}},
{"kind": "item", "key": "none", "qty": None, "ref": {"tab": "sg_list", "key": "A"}},
{"kind": "item", "key": "loop", "qty": 1, "ref": {"tab": "dg_list", "key": "C"}},
]
},
}
LINES = {
"A": {"lines": [
{"kind": "machine", "key": "X1", "qty": 4.295},
{"kind": "hopyo", "key": "B", "qty": 0.5},
{"kind": "material", "key": "M4", "qty": 0.354},
{"kind": "expense", "key": "E16", "qty": 1.103},
]},
"B": {"lines": [{"kind": "labor", "key": "L1", "qty": 1.658},
{"kind": "labor", "key": "L9", "qty": 1}]},
"C": {"lines": [{"kind": "hopyo", "key": "C", "qty": 1}]}, # 스스로 부름 — 깊이 한도에서 멈춤
} # fmt: skip
def test_역전개_엑셀_금액():
s = compute_summary(SHEETS, LINES)
machine = s["sum_machine"][0]
assert (machine["qty"], machine["labor"], machine["material"], machine["expense"]) == (
42.95, 2392315, 1584340, 1435045,
) # fmt: skip
assert machine["sum"] == 5411700
labor = {r["key"]: r for r in s["sum_labor"]}
assert (labor["L1"]["qty"], labor["L1"]["amount"]) == (
8.29,
2222201,
) # 겹 호표 10 × 0.5 × 1.658
assert labor["L9"]["amount"] is None and labor["L9"]["note"] == "목록표에 없음"
material = {r["key"]: (r["qty"], r["amount"]) for r in s["sum_material"]}
assert material == {"M4": (3.54, 5321), "M3": (0.56, 280370)} # 내역 자재 줄 그대로
assert [(r["qty"], r["amount"]) for r in s["sum_expense"]] == [(11.03, 678345)]
def test_호표_구성_없으면_자재_줄만():
s = compute_summary(SHEETS, {})
assert s["sum_labor"] == [] and s["sum_machine"] == [] and s["sum_expense"] == []
assert [r["key"] for r in s["sum_material"]] == ["M3"]
def test_GET_응답에_집계표(tmp_path, monkeypatch):
async def fake_materials(_project_id):
return {"material": {"rows": [{"key": "sand", "group": "골재", "spec": "모래",
"unit": "㎥", "amount": 16}]}} # fmt: skip
monkeypatch.setattr(router_mod, "material_sheets", fake_materials)
body = asyncio.run(router_mod.payload("p", tmp_path))
assert set(body["summary"]) == {"sum_material", "sum_labor", "sum_expense", "sum_machine"}
assert body["summary"]["sum_material"] == [] # 「별도」 · 「관급」 표시 전엔 내역 자재 줄 없음