"""조립(묶음) 줄이 조각의 「일부만」·「밑수 없음」 을 올림 — 2026-09-14 브레인 ㉱ 첫째(구조 결함). 종전 `_composite_row` 는 조각 제목이 **있기만 하면** 금액에 더했음 → 조각 단가가 일부 몫만 섰거나(인력만) 밑수를 모르는 표여도 조립 줄이 **온전한 금액처럼** 섰음. 보통 줄은 그 둘을 막는데 조립 줄만 새던 자리. ⇒ 조각마다 보통 줄과 같은 두 검사 — 걸리면 금액을 안 세우고 어느 조각이 왜인지 사유. """ from __future__ import annotations import pytest from B09_Estimation.B09_Estimation_BillOfQuantities import build_bill from B09_Estimation.B09_Estimation_UnitPrice import cached_build def _composite(parts: list[dict], quantity: str = "1") -> dict: return { "work_items": [ { "work_item_code": None, "name": "시험 묶음", "spec": "", "unit": "개소", "quantity": quantity, "in_bill": True, "composite_parts": parts, "composite_not_ready": [], } ], "materials": [], } def _line(parts: list[dict], quantity: str = "1"): rows = build_bill(_composite(parts, quantity)).rows return next(r for r in rows if r.code is None and r.name == "시험 묶음") def test_일부만_선_조각이_있으면_묶음_금액을_안_세우고_까닭() -> None: build = cached_build() partial = next(c for c in build.partial_ratio if f"B-{c}" in build.book.titles) line = _line([{"code": partial, "quantity": 1}]) assert line.amount_krw is None, (partial, line.amount_krw) assert partial in line.note and "일부만" in line.note, line.note def test_밑수_없는_조각도_같이_막음() -> None: build = cached_build() missing = next(c for c in build.basis_missing if f"B-{c}" in build.book.titles) line = _line([{"code": missing, "quantity": 1}]) assert line.amount_krw is None and "밑수" in line.note, (missing, line.note) def test_온전한_조각만이면_종전대로_금액() -> None: line = _line([{"code": "FP-09-15-02", "quantity": 2}]) assert line.amount_krw and line.amount_krw > 0, line.note # ── 줄 수량이 1 이 아닐 때 — 2026-09-15 랩탑 서브가 찾은 부풀음 ── #: ⭐ **인계 조각 수량은 「합계」다** — B08 계약이고 그쪽 시험이 지킴 #: (BOX 동바리 3.92×10 · 포장 97.66㎥). #: 그래서 `Σ(조각 단가 × 조각 수량)` 이 이미 **그 줄 전체 금액**이다. #: 줄 수량을 또 곱하면 배로 부푼다. 종전 시험이 **줄 수량 1 로만 재서** 못 잡았다 — #: 평떼 718.14㎡ 가 720배(42억)로 섰다. _PART = {"code": "FP-09-15-02", "quantity": 2} @pytest.mark.parametrize("quantity", ["2", "10", "718.14"]) def test_묶음_금액은_줄_수량을_다시_안_곱한다(quantity) -> None: """조각이 합계이므로 **줄 수량이 몇이든 금액은 같다**. 단가는 그 금액 ÷ 수량으로 보인다.""" one = _line([_PART]) many = _line([_PART], quantity) assert one.amount_krw and many.amount_krw, (one.amount_krw, many.amount_krw) assert many.amount_krw == one.amount_krw, (quantity, many.amount_krw, one.amount_krw) assert many.unit_price_krw < one.unit_price_krw, (quantity, many.unit_price_krw) def test_묶음_성분도_줄_수량만큼_안_부푼다() -> None: """재·노·경 셋도 같이 봐야 함 — 합계만 보면 성분이 부푼 것을 놓친다.""" one, many = _line([_PART]), _line([_PART], "10") assert (many.material_krw, many.labor_krw, many.expense_krw) == ( one.material_krw, one.labor_krw, one.expense_krw, )