diff --git a/B08_Quantity/B08_Quantity_Engine_StructurePriceLink.py b/B08_Quantity/B08_Quantity_Engine_StructurePriceLink.py index 6f7aaede..af6fc4f6 100644 --- a/B08_Quantity/B08_Quantity_Engine_StructurePriceLink.py +++ b/B08_Quantity/B08_Quantity_Engine_StructurePriceLink.py @@ -130,11 +130,16 @@ def price_sheets( library: Iterable[dict[str, Any]], manual_all: dict[str, Any] | None = None, ) -> dict[str, dict[str, Any]]: - """B09 내역이 쓸 호표 금액 `{B-AX-ST-…#키: {money, blocked, unconfirmed, reasons}}`. + """B09 내역이 쓸 호표 금액 `{B-AX-ST-…#키: {money, total, blocked, unconfirmed, reasons}}`. - `money` 는 **안 자른** 단위(m)당 3분할 — 자르기는 내역 줄 자리 규칙(B09)이 함. + ⚠ `money` 는 **호표 표의 성분 소계**(칸마다 0.1원 절사한 합) · `total` 은 계금(1원 절사) — + 명세 7장 「호표 안 성분 소계는 절사」. 안 자른 값을 쓰면 구조물도 화면(192,429)과 + 내역 단가(192,430)가 1원 갈림(2026-09-13 검증 프로젝트 실측). """ + from decimal import Decimal + from B08_Quantity.B08_Quantity_Engine_StructureUnitPrice import unit_price_money + from B09_Estimation.B09_Estimation_PriceBook import Money3 prices: dict[str, dict[str, Any]] = {} library = list(library) @@ -149,9 +154,10 @@ def price_sheets( ) if assembled is None: continue - table, money = assembled + table, _exact = assembled prices[structure_price_code(entry["code"], entry["key"])] = { - "money": money, + "money": Money3(*(Decimal(str(table[k])) for k in ("material", "labor", "expense"))), + "total": Decimal(str(table["total"])), "blocked": table["blocked"], "unconfirmed": table["unconfirmed"], "reasons": [ diff --git a/B09_Estimation/B09_Estimation_BillOfQuantities_Rows.py b/B09_Estimation/B09_Estimation_BillOfQuantities_Rows.py index 582aa524..b61f2f95 100644 --- a/B09_Estimation/B09_Estimation_BillOfQuantities_Rows.py +++ b/B09_Estimation/B09_Estimation_BillOfQuantities_Rows.py @@ -140,9 +140,9 @@ def _structure_price_row( ) return row - money = entry["money"] - line = money.scaled(item.quantity) - row.unit_price_krw = round_at(money.total, OutputPlace.UNIT_PRICE_ROW) + # 단가 = 호표 계금(구조물도 화면과 같은 값) · 금액 = 호표 성분 소계 × 수량(명세 7장). + line = entry["money"].scaled(item.quantity) + row.unit_price_krw = entry["total"] row.amount_krw = round_at(line.total, OutputPlace.BOQ_ROW) row.material_krw = line.material row.labor_krw = line.labor diff --git a/resources/tester/test_b09_structure_price_link.py b/resources/tester/test_b09_structure_price_link.py index bbeb6dde..75b83906 100644 --- a/resources/tester/test_b09_structure_price_link.py +++ b/resources/tester/test_b09_structure_price_link.py @@ -104,8 +104,19 @@ def test_내역은_호표_금액에_연장을_곱한다() -> None: row = next(r for r in bill.rows if r.code == entries[0]["code"]) # 모르터 공종 코드가 선 뒤(랩탑 메인 AX-WK) 찰쌓기 호표 세 줄이 다 섬. assert not entry["blocked"], entry["reasons"] - assert row.unit_price_krw == round_at(entry["money"].total, OutputPlace.UNIT_PRICE_ROW) - assert row.amount_krw == round_at(entry["money"].scaled(Decimal(10)).total, OutputPlace.BOQ_ROW) + # 단가 = 구조물도 일위대가 표 계금 그대로(화면과 한 값) · 금액 = 성분 소계 × 연장. + from B08_Quantity.B08_Quantity_Engine_StructureUnitPrice import unit_price_table + + table = unit_price_table( + entries[0]["template"], + entries[0]["sheet"], + _build().book, + lambda code, value: find_variant_code(code, value, _build()), + [load_template("masonry_wet")], + ) + assert row.unit_price_krw == Decimal(str(table["total"])) + cells = sum(Decimal(str(table[k])) for k in ("material", "labor", "expense")) + assert row.amount_krw == round_at(cells * Decimal(10), OutputPlace.BOQ_ROW) assert row.quantity == Decimal(10) and f"호표 {ref}" in row.note