Files
Aislo/resources/tester/test_145_5_b09_summary.py
T
eomsangdonandClaude Opus 5.5 49b780e147 feat(B09): 집계표 역전개를 산근 블록에 이음 · 연결 없음 n(145-5)
- hopyo_lines(sheets) = 산근 블록(145-1 sheets['sg']) · 대가 블록(145-2 daega_blocks) 줄 → 호표 구성
- 산근 → 하위 산근 → 중기 겹 전개 · % 줄 · 빈칸 줄은 자원 아님
- 수량 · 호표 · 호표 구성 없는 내역 항목 = 전개 안 함 · summary.unlinked · 화면 안내 「연결 없음 n」
- 시험 = 엑셀 노무 석공 · 보통인부 · 재료 폭약 · 중기 굴삭기+브레카 반올림 줄

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

158 lines
6.9 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", "unlinked",
} # fmt: skip
assert body["summary"]["sum_material"] == [] # 「별도」 · 「관급」 표시 전엔 내역 자재 줄 없음
# ── 산근 블록(145-1 `sheets['sg']` 꼴)으로 역전개 — 산근 → 하위 산근 → 중기 · 엑셀 반올림 줄 ──
from B09_Estimation.B09_Estimation_Summary import hopyo_lines # noqa: E402
def _ln(kind, tab, key, qty):
return {"kind": kind, "ref": {"tab": tab, "key": key}, "qty": qty, "name": key}
SG = [
{"key": "S1", "lines": [
_ln("labor", "labor", "L3", 1.3701),
_ln("machine", "machine", "X7", 14.3614),
_ln("sangeun", "sg", "S2", 0.5),
{"kind": "extra", "ref": None, "qty": 1.0, "name": "잡재료 2%"}, # % 줄 = 자원 아님
{"kind": "blank", "ref": None, "qty": None, "name": "빈칸"},
]},
{"key": "S2", "lines": [
_ln("labor", "labor", "L6", 4.4008),
_ln("material", "compare", "M7", 80.5194),
]},
] # fmt: skip
SG_SHEETS = {
"labor": [
{"key": "L3", "name": "석공", "price": 258935},
{"key": "L6", "name": "보통인부", "price": 165545},
],
"material": [{"key": "M7", "name": "폭약", "spec": "28m/m", "unit": "KG", "price": 12712}],
"expense": [],
"machine": [{"key": "X7", "name": "굴삭기+브레카(0.7m3)", "labor": 55700, "material": None,
"expense": None}],
"sg": SG,
"bill": {"rows": [
{"kind": "item", "key": "rock", "qty": 100, "ref": {"tab": "sg_list", "key": "S1"}},
{"kind": "item", "key": "earth", "qty": 5, "ref": None}, # 조합 밖 토공
{"kind": "item", "key": "st", "qty": 2, "ref": {"tab": "dg_list", "key": "st:x"}},
{"kind": "item", "key": "none", "qty": None, "ref": {"tab": "sg_list", "key": "S1"}},
]},
} # fmt: skip
def test_산근_블록_역전개_엑셀_반올림():
lines = hopyo_lines(SG_SHEETS)
assert lines["S1"]["lines"] == [
{"kind": "labor", "key": "L3", "qty": 1.3701},
{"kind": "machine", "key": "X7", "qty": 14.3614},
{"kind": "hopyo", "key": "S2", "qty": 0.5},
]
s = compute_summary(SG_SHEETS, lines)
labor = {r["name"]: (r["qty"], r["amount"]) for r in s["sum_labor"]}
# 엑셀 노무비수량금액집계표 6 · 9 줄(ROUND — 220.04 × 165,545 = 36,426,521.8 → …522)
assert labor == {"석공": (137.01, 35476684), "보통인부": (220.04, 36426522)}
# 재료비수량금액집계표 8 줄 폭약 4,025.97 × 12,712 = 51,178,130.6 → 51,178,131
assert [(r["qty"], r["amount"]) for r in s["sum_material"]] == [(4025.97, 51178131)]
machine = s["sum_machine"][0] # 중기시간금액집계표 10 줄 노무비
assert (machine["qty"], machine["labor"], machine["sum"]) == (1436.14, 79992998, None)
assert s["unlinked"] == 3 # 토공 · 대가 전 구조물 · 수량 빈칸 = 전개 안 함