feat(b09): 호표 갈래 뒤집기 — 일위대가화 줄 = 대가 · 단가산출 줄 = 산근(155-3 · 155-4)
- 일위대가목록 = 구조물 조합 + 사전조합 + 일위대가화(승격 · 옛 SP) 줄 · 단가산출 줄 = 산근 줄 - 산근 줄 수량 = 수량 그대로(배 ≠ 1 로직 금액 1/배 흠) · 대가 줄만 ÷ 배 - 품셈 블록 로직 줄 판정 = 산근 풀이와 같은 call_of(기계경비 = 중기 · 그 밖 = 하위 산근) - 비고 「산근 n호표」(Calc_Labor 파이썬 · TS 짝) · 산근 줄 누름 = 단가산출근거 탭 - 산근목록 원천 = 단가산출 줄 + 사전조합 안 줄 + 일위대가화 줄 하위 로직(노무 · 자재 · 중기 줄 제외) - 산근 자재 자리표시 키(MO:경유:…) = 자재단가대비표 키로 - 시험: 145-1 다섯 · 145-2 셋 새 갈래로(머리에 근거 한 줄) · 새 test_155_b09_sources Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015KYZ2HWWdUwZWYnn3tQz7X
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""145-1 B09 단가산출근거 · 목록표 — 승격 줄 → 산근 블록 · 엑셀 검산 · 같은 산근 하나 · 빈 표.
|
||||
"""145-1 B09 단가산출근거 · 목록표 — 단가산출 줄 → 산근 블록 · 엑셀 검산 · 같은 산근 하나 · 빈 표.
|
||||
|
||||
- 산근 = 연결된 사본 일위대가 로직 줄 중 `승격: true` + 옛 SP 참조 줄(134-2 ③) · 승격 아닌 줄은 안 담음
|
||||
- 산근 = 155-4 갈래(사용자 뜻 · 145 정함 뒤집음) — 일위대가 로직 줄 중 단가산출 줄(승격 아님) +
|
||||
사전조합 안 단가산출 줄 · 일위대가화(승격 · 옛 SP) 줄 자체는 산근 아님(대가 블록)
|
||||
- 줄 금액 = ROUNDDOWN(B09 굳힌 단가 × 1 단위 수량, 1) · 총계 = 비목마다 ROUNDDOWN 0 — 엑셀
|
||||
단가산출근거 제 6호표(뒷채움) 줄 값 · 총계 8,328(6,878 · 538 · 912)과 같음
|
||||
- 같은 로직 + 같은 입력값 = 산근 하나(쓰는 구조물이 늘어남) · 입력이 다르면 둘 · 번호 = 첫 등장
|
||||
@@ -29,6 +30,7 @@ STONE = "GF000219" # 돌쌓기 메쌓기(인력) — 석공 0.195 · 보통인
|
||||
NET = "GC000337" # 낙석방지망 — 노무 · 크레인(중기) · 자재 셋
|
||||
NET_IN = {"구분": "기계식", "방지망": "포켓식", "지역": "전국평균", "크레인": "2104-0010"}
|
||||
SP_REF = {"층": "system", "주인": None, "키": "SP-000001"}
|
||||
SC_REF = {"층": "system", "주인": None, "키": "SC-000001"}
|
||||
|
||||
|
||||
def _snapshot(folder: Path, rows, values=None, copies=None) -> Path:
|
||||
@@ -52,8 +54,8 @@ def _sheets(*folders: Path) -> dict:
|
||||
return compute_base(prices.build_inputs(None, list(dict.fromkeys(keys))), {})
|
||||
|
||||
|
||||
def test_승격_줄만_산근_블록_단가는_B09_표(tmp_path):
|
||||
a = _snapshot(tmp_path / "a", [{"로직": STONE, "승격": True}, {"로직": NET}], {NET: NET_IN})
|
||||
def test_단가산출_줄만_산근_블록_단가는_B09_표(tmp_path):
|
||||
a = _snapshot(tmp_path / "a", [{"로직": STONE}, {"로직": NET, "승격": True}], {NET: NET_IN})
|
||||
sheets = _sheets(a)
|
||||
got = sg.collect([("s1", a)], sheets)
|
||||
(block,) = got["sg"]
|
||||
@@ -75,7 +77,7 @@ def test_승격_줄만_산근_블록_단가는_B09_표(tmp_path):
|
||||
|
||||
|
||||
def test_중기는_중기목록표_노_재_경_자재는_적용_단가(tmp_path):
|
||||
a = _snapshot(tmp_path / "a", [{"로직": NET, "승격": True}], {NET: NET_IN})
|
||||
a = _snapshot(tmp_path / "a", [{"로직": NET}], {NET: NET_IN})
|
||||
sheets = _sheets(a)
|
||||
(block,) = sg.collect([("s1", a)], sheets)["sg"]
|
||||
kinds = [ln["kind"] for ln in block["lines"]]
|
||||
@@ -104,21 +106,26 @@ def test_엑셀_6호표_뒷채움_검산():
|
||||
assert sg.block_totals([labor, {**machine, "material": None}])["sum"] is None # 빈칸 = 빈칸
|
||||
|
||||
|
||||
def test_같은_로직_같은_입력은_하나_다르면_둘_옛_SP_도_같은_산근(tmp_path):
|
||||
a = _snapshot(tmp_path / "a", [{"로직": NET, "승격": True}], {NET: NET_IN})
|
||||
b = _snapshot(tmp_path / "b", [{"로직": NET, "승격": True}], {NET: {**NET_IN, "": ""}})
|
||||
def test_같은_로직_같은_입력은_하나_다르면_둘_사전조합_안_줄도_같은_산근(tmp_path):
|
||||
a = _snapshot(tmp_path / "a", [{"로직": NET}], {NET: NET_IN})
|
||||
b = _snapshot(tmp_path / "b", [{"로직": NET}], {NET: {**NET_IN, "": ""}})
|
||||
other = {**NET_IN, "방지망": "비포켓식"}
|
||||
c = _snapshot(tmp_path / "c", [{"로직": NET, "승격": True}], {NET: other})
|
||||
sp = {"담은로직": [{"로직": NET, "입력값": NET_IN}], "배": 1}
|
||||
c = _snapshot(tmp_path / "c", [{"로직": NET}], {NET: other})
|
||||
sc = {"이름": "사전조합", "담은로직": [{"로직": NET, "입력값": NET_IN}]}
|
||||
sp = {"담은로직": [{"로직": NET, "입력값": other}], "배": 1} # 옛 SP = 일위대가화 = 산근 아님
|
||||
d = _snapshot(
|
||||
tmp_path / "d",
|
||||
[{"로직": "", "참조": SP_REF}, {"로직": STONE, "승격": True}],
|
||||
copies={"참조|system||SP-000001": sp},
|
||||
[{"로직": "", "참조": SC_REF}, {"로직": "", "참조": SP_REF}, {"로직": STONE}],
|
||||
copies={"참조|system||SC-000001": sc, "참조|system||SP-000001": sp},
|
||||
)
|
||||
got = sg.collect([("s1", a), ("s2", b), ("s3", c), ("s4", d)], _sheets(a, c, d))
|
||||
blocks = got["sg"]
|
||||
assert [(x["no"], x["logic"], x["uses"]) for x in blocks] == [
|
||||
(1, NET, ["s1", "s2", "s4"]), # 빈 입력 칸은 무시 · 옛 SP(같은 로직 · 입력) = 같은 산근
|
||||
(
|
||||
1,
|
||||
NET,
|
||||
["s1", "s2", "s4"],
|
||||
), # 빈 입력 칸은 무시 · 사전조합 안 줄(같은 로직 · 입력) = 같은 산근
|
||||
(2, NET, ["s3"]),
|
||||
(3, STONE, ["s4"]),
|
||||
]
|
||||
@@ -130,7 +137,7 @@ def test_사본_없으면_빈_표_연결_없음(tmp_path, monkeypatch):
|
||||
got = sg.collect([("s1", tmp_path / "없음")], {})
|
||||
assert got["sg"] == [] and got["sg_list"] == [] and got["sg_gaps"] == []
|
||||
assert got["sg_notes"] == [sg.NO_SOURCE, sg.EMPTY]
|
||||
plain = _snapshot(tmp_path / "p", [{"로직": STONE}]) # 승격 없는 사본도 빈 표
|
||||
plain = _snapshot(tmp_path / "p", [{"로직": STONE, "승격": True}]) # 일위대가화만 = 빈 표
|
||||
assert sg.collect([("s1", plain)], {})["sg"] == []
|
||||
|
||||
async def fake_materials(_project_id):
|
||||
@@ -143,7 +150,7 @@ def test_사본_없으면_빈_표_연결_없음(tmp_path, monkeypatch):
|
||||
|
||||
|
||||
def test_단가_없으면_빈칸과_연결_없음(tmp_path):
|
||||
a = _snapshot(tmp_path / "a", [{"로직": STONE, "승격": True}])
|
||||
a = _snapshot(tmp_path / "a", [{"로직": STONE}])
|
||||
got = sg.collect([("s1", a)], {"labor": [], "machine": [], "compare": []})
|
||||
(block,) = got["sg"]
|
||||
assert block["labor"] is None and block["sum"] is None
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
"""145-2 B09 일위대가표 · 일위대가목록표 — 엑셀 검산 대조 ·
|
||||
거울(`B09_Estimation_Calc_Labor.py` ↔ `.ts`) ·
|
||||
견본 사본 → 대가 블록(`B09_Estimation_Labor_Source`).
|
||||
견본 사본 단정 = 155-3 갈래(사용자 뜻 · 145 정함 뒤집음) — 일위대가화(승격) 줄 = 대가(품셈) 블록 ·
|
||||
단가산출 줄 = 산근 줄(수량 그대로) · 사전조합 = 겹 대가 · 비고 「산근 n호표」.
|
||||
|
||||
엑셀 견본 = 기번3 공통 xlsx 「일위대가표」 같은 입력:
|
||||
대가1 모르터바르기 109,259 · 대가2 파형강관부설 D800 16,001(15,002 · 584 · 415 · 공구손료 2 %) ·
|
||||
@@ -22,6 +24,7 @@ if str(ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from B09_Estimation import B09_Estimation_Prices as prices # noqa: E402
|
||||
from B09_Estimation import B09_Estimation_Sangeun as sangeun # noqa: E402
|
||||
from B09_Estimation.B09_Estimation_Calc_Base import compute_base # noqa: E402
|
||||
from B09_Estimation.B09_Estimation_Calc_Labor import compute_labor, daega_price # noqa: E402
|
||||
from B09_Estimation.B09_Estimation_Labor_Source import daega_inputs # noqa: E402
|
||||
@@ -257,8 +260,10 @@ def project(tmp_path_factory) -> Path:
|
||||
_snap(root, "pipe@100.0", "SS000901", LINES, _values(STONE))
|
||||
_snap(root, "pipe@200.0", "SS000901", LINES, _values(STONE)) # 같은 원본 + 같은 입력값
|
||||
_snap(root, "pipe@300.0", "SS000901", LINES, _values({**STONE, "높이": 5})) # 입력값 다름
|
||||
promoted = [
|
||||
promoted = [ # 일위대가화 줄 셋(인력 · 중기 · 재료 + % 줄) + 사전조합
|
||||
{"로직": "GF000219", "승격": True, "수량": {"손": 1}},
|
||||
{"로직": "GF000119", "승격": True, "수량": {"손": 1}},
|
||||
{"로직": "GF000143", "승격": True, "수량": {"손": 1}},
|
||||
{"참조": {"층": "system", "주인": None, "키": "SC-T"}, "수량": {"손": 4}},
|
||||
]
|
||||
_snap(root, "pipe@400.0", "SS000902", promoted, _values(STONE), SC)
|
||||
@@ -274,14 +279,18 @@ def test_견본_사본_대가_블록(project):
|
||||
owners["pipe@100.0"] == owners["pipe@200.0"] != owners["pipe@300.0"]
|
||||
) # 같은 원본+입력 = 한 호표
|
||||
blocks = {b["id"]: b for b in got["blocks"]}
|
||||
assert [b["kind"] for b in got["blocks"]] == ["조합"] * 3 + ["사전조합"] + ["품셈"] * 4
|
||||
# 단가산출 줄은 대가 항목 아님 → 품셈 블록 = 일위대가화 줄 셋만
|
||||
assert [b["kind"] for b in got["blocks"]] == ["조합"] * 3 + ["사전조합"] + ["품셈"] * 3
|
||||
a = blocks[owners["pipe@100.0"]]
|
||||
assert [(ln["kind"], ln["qty"]) for ln in a["lines"]] == [
|
||||
("daega", 2),
|
||||
("daega", 0.5),
|
||||
("daega", 1),
|
||||
assert [(ln["kind"], ln["key"][:11], ln["qty"]) for ln in a["lines"]] == [
|
||||
("sangeun", "SG:GF000219", 2),
|
||||
("sangeun", "SG:GF000119", 0.5),
|
||||
("sangeun", "SG:GF000143", 1),
|
||||
]
|
||||
stone, digger, crane = (blocks[ln["key"]] for ln in a["lines"])
|
||||
b = blocks[owners["pipe@400.0"]]
|
||||
assert [ln["kind"] for ln in b["lines"]] == ["daega"] * 4
|
||||
stone, digger, crane = (blocks[ln["key"]] for ln in b["lines"][:3])
|
||||
assert stone["kind"] == "품셈" and stone["name"] == "돌쌓기 메쌓기"
|
||||
assert [(ln["kind"], ln["key"]) for ln in stone["lines"]] == [
|
||||
("labor", "LB000033"),
|
||||
("labor", "LB000002"),
|
||||
@@ -290,13 +299,13 @@ def test_견본_사본_대가_블록(project):
|
||||
assert digger["lines"][0]["kind"] == "machine" and digger["lines"][0]["key"].startswith("EQ")
|
||||
pct = crane["lines"][-1]
|
||||
assert pct["kind"] == "pct" and pct["base"] == "part:material" and pct["qty"] == 20
|
||||
b = blocks[owners["pipe@400.0"]]
|
||||
assert b["lines"][0]["kind"] == "sangeun" and b["lines"][0]["key"].startswith("SG:GF000219:")
|
||||
sc = blocks[b["lines"][1]["key"]]
|
||||
assert sc["kind"] == "사전조합" and b["lines"][1]["qty"] == 4
|
||||
sc = blocks[b["lines"][3]["key"]]
|
||||
assert sc["kind"] == "사전조합" and b["lines"][3]["qty"] == 4
|
||||
assert (
|
||||
sc["lines"][0]["key"] == stone["id"] and sc["lines"][0]["qty"] == 3
|
||||
) # 겹 대가 = 같은 품셈
|
||||
sc["lines"][0]["kind"] == "sangeun"
|
||||
and sc["lines"][0]["key"] == a["lines"][0]["key"]
|
||||
and sc["lines"][0]["qty"] == 3
|
||||
) # 사전조합 안 단가산출 줄 = 같은 산근(같은 로직 + 같은 입력값)
|
||||
|
||||
|
||||
def test_견본_사본_끝까지_셈(project):
|
||||
@@ -304,14 +313,18 @@ def test_견본_사본_끝까지_셈(project):
|
||||
inputs = prices.build_inputs(None, keys)
|
||||
inputs["daega"] = daega_inputs(project)
|
||||
base = compute_base(inputs, {})
|
||||
out = compute_labor(inputs["daega"], base)
|
||||
sg_rows = {r["key"]: r for r in sangeun.build(project, base)["sg_list"]}
|
||||
out = compute_labor(inputs["daega"], base, sg_rows)
|
||||
owner = inputs["daega"]["owners"]["pipe@100.0"]
|
||||
combo = _block(out, owner)
|
||||
assert combo["sum"] is not None and combo["no"] == 1
|
||||
# 조합 줄 = 부른 품셈 합계 × 수량(ROUNDDOWN 1) · 합계 = ROUNDDOWN 0
|
||||
# 조합 줄 = 산근 단가 × 수량(ROUNDDOWN 1) · 비고 「산근 n호표」 · 합계 = ROUNDDOWN 0
|
||||
for ln in combo["lines"]:
|
||||
child = _block(out, ln["key"])
|
||||
assert ln["prices"]["labor"] == child["labor"]
|
||||
row = sg_rows[ln["key"]]
|
||||
assert ln["prices"]["labor"] == row["labor"] and ln["ref_no"] == f"산근 {row['no']}호표"
|
||||
# 일위대가화 블록 = 금액 다 참(기계 줄 빈칸 없음)
|
||||
for ln in _block(out, inputs["daega"]["owners"]["pipe@400.0"])["lines"]:
|
||||
assert _block(out, ln["key"])["sum"] is not None, ln["name"]
|
||||
price = daega_price(out, "pipe@100.0")
|
||||
assert price["no"] == "대가1" and price["sum"] == combo["sum"]
|
||||
assert daega_price(out, "pipe@500.0") is None # 사본 없음 = 없음
|
||||
@@ -367,7 +380,7 @@ def test_집계표_이음_꼴():
|
||||
|
||||
def test_한_바퀴_내역_대가_산근_중기_집계표(tmp_path):
|
||||
"""이음 끝 확인 — 설계내역서 구조물 줄(B08 열 owners) → 대가 블록마다 나눔 → 대가 줄 →
|
||||
산근(145-1 · 승격 줄) → 중기 → 집계표(145-5) 역전개 · 짝 셈 한 벌 = Router.compute_sheets."""
|
||||
산근(155-3 · 단가산출 줄) → 중기 → 집계표(145-5) 역전개 · 짝 셈 한 벌 = Router.compute_sheets."""
|
||||
from B09_Estimation.B09_Estimation_Calc_Base import rounddown
|
||||
from B09_Estimation.B09_Estimation_Router import compute_sheets
|
||||
from B09_Estimation.B09_Estimation_Summary import compute_summary, hopyo_lines
|
||||
@@ -375,7 +388,7 @@ def test_한_바퀴_내역_대가_산근_중기_집계표(tmp_path):
|
||||
root = _project(tmp_path / "loop", [(100, "SS000901"), (400, "SS000902")])
|
||||
_snap(root, "pipe@100.0", "SS000901", LINES, _values(STONE))
|
||||
promoted = [
|
||||
{"로직": "GF000119", "승격": True, "수량": {"손": 1}}, # 노체포설 = 산근 · 중기 줄
|
||||
{"로직": "GF000119", "승격": True, "수량": {"손": 1}}, # 노체포설 = 일위대가화 · 중기 줄
|
||||
{"참조": {"층": "system", "주인": None, "키": "SC-T"}, "수량": {"손": 4}},
|
||||
]
|
||||
_snap(root, "pipe@400.0", "SS000902", promoted, _values(STONE), SC)
|
||||
@@ -400,11 +413,14 @@ def test_한_바퀴_내역_대가_산근_중기_집계표(tmp_path):
|
||||
block = _block(sheets, row["ref"]["key"])
|
||||
assert row["price_labor"] == block["labor"] and block["sum"] is not None
|
||||
assert row["labor"] == rounddown(row["qty"] * block["labor"], 0)
|
||||
# 산근 이음 — 대가 줄 키 = 145-1 산근 키 · 단가가 들어옴
|
||||
combo = _block(sheets, owners["pipe@400.0"])
|
||||
# 산근 이음 — 단가산출 줄 키 = 145-1 산근 키 · 단가가 들어옴
|
||||
combo = _block(sheets, owners["pipe@100.0"])
|
||||
sg_line = combo["lines"][0]
|
||||
sg_row = next(r for r in sheets["sg_list"] if r["key"] == sg_line["key"])
|
||||
assert sg_line["kind"] == "sangeun" and sg_line["prices"]["labor"] == sg_row["labor"]
|
||||
# 일위대가화 줄 = 대가 블록 · 기계 줄 = 중기
|
||||
digger = _block(sheets, _block(sheets, owners["pipe@400.0"])["lines"][0]["key"])
|
||||
assert digger["kind"] == "품셈" and digger["lines"][0]["kind"] == "machine"
|
||||
# 집계표 역전개 — 대가 · 사전조합 · 품셈 · 산근 끝까지
|
||||
summary = compute_summary(sheets, hopyo_lines(sheets))
|
||||
assert summary["unlinked"] == 0
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
"""155-3 · 155-4 B09 호표 갈래 — 사용자 뜻(10-9 둘째 · 145 정함 뒤집음 · M02 구분 짝 · 엑셀 꼴):
|
||||
일위대가화(승격) 줄 = 일위대가 호표(대가 블록) · 단가산출 줄 = 산근 ·
|
||||
일위대가화 줄의 하위 로직 줄 = 산근(대가 블록 산근 줄 키 = 산근 목록 키) ·
|
||||
산근 줄 수량 = 수량 그대로(배 10 로직 금액 1/배 흠) · 비고 「산근 n호표」(파이썬 · TS 짝) ·
|
||||
산근 자재 자리표시 키(「MO:경유:…」) = 자재단가대비표 키.
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
for path in (ROOT, ROOT / "resources" / "tester"):
|
||||
if str(path) not in sys.path:
|
||||
sys.path.insert(0, str(path))
|
||||
|
||||
import test_145_2_b09_labor_calc as t145 # noqa: E402
|
||||
|
||||
from B09_Estimation import B09_Estimation_Prices as prices # noqa: E402
|
||||
from B09_Estimation import B09_Estimation_Sangeun as sg # noqa: E402
|
||||
from B09_Estimation.B09_Estimation_Calc_Base import compute_base, rounddown # noqa: E402
|
||||
from B09_Estimation.B09_Estimation_Calc_Labor import compute_labor # noqa: E402
|
||||
from B09_Estimation.B09_Estimation_Labor_Source import daega_inputs # noqa: E402
|
||||
from B09_Estimation.B09_Estimation_Linked import linked_resource_keys, used_keys # noqa: E402
|
||||
from B09_Estimation.B09_Estimation_Router import compute_sheets # noqa: E402
|
||||
from M02_MasterTemplete import M02_Item_Prices as item_prices # noqa: E402
|
||||
|
||||
PILE = "GF000233" # 말뚝 다듬기 — 원/10개(배 10) · 인력 줄 둘
|
||||
PIPE = "GF000174" # 흄관 부설 — 하위 로직 줄(GF000155 · GF000160) = 하위 산근
|
||||
FUEL = "GF000011" # 재료 줄 「MO:경유:{지역}」 자리표시
|
||||
|
||||
|
||||
def _sheets_of(root):
|
||||
inputs = prices.build_inputs(None, linked_resource_keys(root))
|
||||
inputs["daega"] = daega_inputs(root)
|
||||
inputs["hopyo"] = {}
|
||||
inputs["bill"] = {"items": []}
|
||||
return inputs, compute_sheets(root, inputs, {})
|
||||
|
||||
|
||||
def _blk(sheets, key):
|
||||
return next(b for b in sheets["daega_blocks"] if b["key"] == key)
|
||||
|
||||
|
||||
def test_155_3_배_10_로직_산근_줄_수량_그대로_대가_줄만_나눔(tmp_path):
|
||||
root = t145._project(tmp_path / "p", [(100, "SS000901"), (200, "SS000902")])
|
||||
t145._snap(root, "pipe@100.0", "SS000901", [{"로직": PILE, "수량": {"손": 20}}], {})
|
||||
t145._snap(
|
||||
root, "pipe@200.0", "SS000902", [{"로직": PILE, "승격": True, "수량": {"손": 20}}], {}
|
||||
)
|
||||
inputs, sheets = _sheets_of(root)
|
||||
owners = sheets["daega_owners"]
|
||||
(sg_line,) = _blk(sheets, owners["pipe@100.0"])["lines"]
|
||||
(dg_line,) = _blk(sheets, owners["pipe@200.0"])["lines"]
|
||||
assert (sg_line["kind"], sg_line["qty"]) == ("sangeun", 20) # 산근 단가 = 1 개
|
||||
assert (dg_line["kind"], dg_line["qty"]) == ("daega", 2) # 대가 블록 = 10 개
|
||||
row = next(r for r in sheets["sg_list"] if r["key"] == sg_line["key"])
|
||||
assert sg_line["ref_no"] == f"산근 {row['no']}호표" and dg_line["ref_no"].startswith("대가")
|
||||
# 같은 일 · 같은 수량 = 금액 거의 같음(끝수 자리만 다름 · 예전 흠 = 1/10)
|
||||
assert sg_line["amount"] is not None and dg_line["amount"] is not None
|
||||
assert abs(sg_line["amount"] - dg_line["amount"]) <= 0.02 * dg_line["amount"]
|
||||
|
||||
|
||||
def test_155_4_일위대가화_하위_로직_줄_산근_목록에(tmp_path):
|
||||
root = t145._project(tmp_path / "p", [(100, "SS000901")])
|
||||
t145._snap(
|
||||
root, "pipe@100.0", "SS000901", [{"로직": PIPE, "승격": True, "수량": {"손": 1}}], {}
|
||||
)
|
||||
inputs, sheets = _sheets_of(root)
|
||||
(call,) = _blk(sheets, sheets["daega_owners"]["pipe@100.0"])["lines"]
|
||||
pipe = _blk(sheets, call["key"])
|
||||
assert pipe["kind"] == "품셈" and call["kind"] == "daega"
|
||||
subs = [ln for ln in pipe["lines"] if ln["kind"] == "sangeun"]
|
||||
listed = {r["key"]: r for r in sheets["sg_list"]}
|
||||
assert {ln["key"][:11] for ln in subs} >= {"SG:GF000155", "SG:GF000160"}
|
||||
for ln in subs: # 대가 블록 산근 줄 = 산근 목록 줄(같은 키) · 비고 · 단가 이음
|
||||
assert ln["key"] in listed and ln["ref_no"] == f"산근 {listed[ln['key']]['no']}호표"
|
||||
assert ln["prices"]["labor"] == listed[ln["key"]]["labor"]
|
||||
assert all(r["logic"] != PIPE for r in sheets["sg_list"]) # 일위대가화 줄 자체 = 산근 아님
|
||||
|
||||
|
||||
def test_155_4_산근_자재_자리표시_키_대비표로(tmp_path):
|
||||
folder = tmp_path / "a"
|
||||
folder.mkdir()
|
||||
files = {
|
||||
"단가.json": item_prices.to_json(item_prices.collect([FUEL])),
|
||||
"일위대가.json": {"줄": {"종류": "일위대가", "담은로직": [{"로직": FUEL}]}},
|
||||
"입력값.json": {},
|
||||
}
|
||||
for name, data in files.items():
|
||||
(folder / name).write_text(json.dumps(data, ensure_ascii=False), encoding="utf-8")
|
||||
sheets = compute_base(prices.build_inputs(None, used_keys(folder)), {})
|
||||
(block,) = sg.collect([("s1", folder)], sheets)["sg"]
|
||||
fuel = [ln for ln in block["lines"] if ln["kind"] == "material"]
|
||||
assert fuel and all(ln["ref"]["key"].startswith("MO0") for ln in fuel)
|
||||
applied = {r["key"]: r["applied"] for r in sheets["compare"]}
|
||||
for ln in fuel:
|
||||
assert ln["material"] == rounddown(applied[ln["ref"]["key"]] * ln["qty"], 1)
|
||||
|
||||
|
||||
def test_155_3_비고_산근_n호표_거울():
|
||||
sangeun = {k: {**v, "no": n} for n, (k, v) in enumerate(t145.SANGEUN.items(), 1)}
|
||||
py = json.loads(json.dumps(compute_labor(t145.EXCEL, t145.SHEETS, sangeun), ensure_ascii=False))
|
||||
ts = t145._ts(t145.EXCEL, t145.SHEETS, sangeun)
|
||||
assert t145._same(py, ts["out"])
|
||||
notes = [
|
||||
ln.get("ref_no") for b in py["daega_blocks"] for ln in b["lines"] if ln["kind"] == "sangeun"
|
||||
]
|
||||
assert notes and notes[0] == "산근 1호표" and all(n and n.startswith("산근 ") for n in notes)
|
||||
Reference in New Issue
Block a user