diff --git a/B08_Quantity/B08_Quantity_Engine_Handoff_Rows.py b/B08_Quantity/B08_Quantity_Engine_Handoff_Rows.py index a0ce263f..eaa403f0 100644 --- a/B08_Quantity/B08_Quantity_Engine_Handoff_Rows.py +++ b/B08_Quantity/B08_Quantity_Engine_Handoff_Rows.py @@ -131,9 +131,22 @@ def _earthwork_rows( elif mismatch is not None: spec_detail = f"집계 {amount:,.2f} {unit} (품셈 밑수 {mismatch[0]})" unit, amount = mismatch[0], 0.0 + # ⚠⚠ **코드가 없으면 반드시 막힘 표시를 단다**(2026-09-09 랩탑 메인 제보). + # 종전에는 `unmatched_work_items` 목록과 `bill_flag_warnings` 에만 실려, + # **줄 단위로 보는 쪽**(B09·화면)이 「코드도 없고 막힘 표시도 없는 멀쩡한 줄」로 + # 읽었다 — 금액이 조용히 빠졌다(측구터파기·흙깎기 「굴삭기+브레카」). + # ⇒ 오늘 세운 규칙 「막혔다고 말하기 전에 `blocked_kind` 를 볼 것」의 **뒤집힌 얼굴**: + # 보는 쪽을 고쳤으면 **다는 쪽도** 빠짐없이 달아야 한다. + blocked_kind = mismatch[1] if mismatch else None + blocked_reason = mismatch[2] if mismatch else "" if code is None and not is_subtotal: label = f"{group}({ground})" if ground else group unmatched.append(f"{label} — {method_note}" if method_note else label) + if blocked_kind is None: + # 시공법을 고르면 풀리는 자리와, 품셈을 아직 못 이은 자리를 **갈라 적는다** — + # 다음에 할 일이 다르다(하나는 사용자 입력, 하나는 매핑 작업). + blocked_kind = BLOCKED_INPUT_MISSING if method_note else BLOCKED_UNIT_DATA_MISSING + blocked_reason = method_note or f"{label} 의 품셈 공종을 아직 못 이었습니다" rows.append( { "work_item_code": code, @@ -162,9 +175,9 @@ def _earthwork_rows( "secondary_axes": None, "spec_class": None, "spec_class_basis": "", - # 토공 줄도 막힐 수 있다 — 품셈 밑수와 단위가 다르면 그 사유가 실린다. - "blocked_kind": mismatch[1] if mismatch else None, - "blocked_reason": mismatch[2] if mismatch else "", + # 토공 줄도 막힐 수 있다 — 밑수 어긋남과 **코드 없음** 둘 다 여기 실린다. + "blocked_kind": blocked_kind, + "blocked_reason": blocked_reason, "composite_not_ready": None, # 합계 줄과 무대 줄은 값은 내되 내역에 안 선다. "in_bill": bool(row.get("in_bill", True)) and not is_subtotal and mismatch is None, @@ -191,6 +204,13 @@ def _haul_rows( in_bill = bool(row.get("in_bill", True)) and entry.get("in_bill", True) if code is None and in_bill: unmatched.append(f"운반({equipment})") + # ⚠ 코드가 없으면 **줄에 막힘 표시를 단다**(2026-09-09) — 목록에만 실으면 줄 단위로 + # 보는 쪽이 「멀쩡한 줄」로 읽어 금액이 조용히 빠진다(도자운반·덤프운반이 그랬다). + # ⚠ `in_bill` 이 False 인 무대 줄은 **막힌 것이 아니다** — 품에 포함이라 안 세우는 것. + haul_blocked = BLOCKED_UNIT_DATA_MISSING if (code is None and in_bill) else None + haul_blocked_reason = ( + f"운반({equipment})의 품셈 공종을 아직 못 이었습니다" if haul_blocked else "" + ) # ⚠⚠ **내역서 수량은 자연상태다** — 유토곡선은 다짐상태로 쌓고(운반거리를 그 기준으로 # 재야 맞는다) 내역에 오르는 수량은 되돌린 값이다(`config_system_design` 5-4-3 # 「운반거리 산정 시 모든 수량은 다짐상태로 환산해 계산하고, **내역서에 적용하는 @@ -242,8 +262,8 @@ def _haul_rows( "secondary_axes": None, "spec_class": None, "spec_class_basis": "", - "blocked_kind": None, - "blocked_reason": "", + "blocked_kind": haul_blocked, + "blocked_reason": haul_blocked_reason, "composite_not_ready": None, "in_bill": in_bill, "in_bill_reason": str(entry.get("reason") or ""),