"""준비공 — 부대시설·가설공사 다섯 줄. ⚠ `B08_Quantity_Engine_Preparation` 에서 갈라 나온 파일이다(2026-09-09, 700줄 제한). **내용은 그대로 옮겼고 규칙도 그대로다** — 개소를 지어내지 않고, 「품셈에 공종이 없음」과 「개소 미입력」을 갈라 적으며, 값이 없어도 줄은 세운다. """ from __future__ import annotations from typing import Any from B08_Quantity.B08_Quantity_Engine_Preparation_Status import ( REASON_NO_WORK_ITEM, STATUS_PENDING, STATUS_READY, ) #: 부대시설·가설공사 — **법이 요구하는데 우리가 안 내던 다섯 줄**(2026-09-09 사용자 확정 ⑬). #: `key` 는 설정의 `ancillary_counts` 칸 이름, `code` 는 품셈 공종(없으면 `None`). #: ⚠ 다섯 중 **품셈에 공종이 있는 것은 가설창고 하나뿐**이다(마스터 전수 확인). #: 나머지 넷은 **금액이 못 선다** — 줄은 세우되 「공종 자체가 품셈에 없음」이라고 적는다. #: 「아직 안 만든 것」과 갈라 적어야 다음에 할 일이 달라진다. ANCILLARY_ITEMS: tuple[dict[str, Any], ...] = ( { "key": "national_point_sign", "item": "국가지점번호판", "unit": "개소", "code": None, "legal": True, "why": ( "⚠ 법정 의무 — 임도규정 제26조제5항 「국가지점번호판을 제작하여 500미터 마다 " "설치·관리하되, 필요시 거리를 조정할 수 있으며」. ⚠ 기점 포함·종점 잔여·갈림길 " "중복을 원문이 정하지 않아 **연장÷500 을 산식으로 쓰지 않는다** — 개소를 넣으면 섬" ), }, { "key": "guide_sign", "item": "임도 안내판", "unit": "개소", "code": None, "legal": True, "why": ( "⚠ 법정 의무 — 임도규정 제26조제6항 「임도의 시점 및 종점에 안내판…을 설치하여야 " "한다」. 노선을 이어 가면 최초 시점·최종 종점에 둘 수 있어 **개소는 설계 판단**임" ), }, { "key": "gate", "item": "차단기", "unit": "개소", "code": None, "legal": False, "why": "임도기술교본 11장(실무 참고) — 법령·행정규칙에는 없음. 설치 개소는 설계 판단", }, { "key": "site_container", "item": "가설창고(컨테이너)", "unit": "개소", "code": "FP-11-01", "legal": False, "why": "품셈 11-1 콘테이너형 가설건축물 — ⚠ 개소는 현장 조건이라 설계가 정함", }, { "key": "flood_supplies", "item": "수방대책 자재", "unit": "식", "code": None, "legal": False, "why": ( "임도기술교본 11장 — 비닐·말뚝·마대·삽 등을 현장 입구에 비치. " "품목·수량이 원문에 없어 **한 벌(식)로 받는다**" ), }, ) #: 품셈에 그 이름의 공종이 아예 없는 줄에 적는 사유. 「아직 안 만든 것」과 갈라 쓴다. def ancillary_rows(counts: dict[str, Any] | None = None) -> list[dict[str, Any]]: """부대시설·가설공사 줄. **개소를 안 넣어도 줄은 선다** — 빠진 것이 보이게. ⚠ 개소를 **지어내지 않는다**(확정 ⑬). 설정 `ancillary_counts` 에 넣은 값만 쓴다. """ given = {str(key): value for key, value in (counts or {}).items()} rows: list[dict[str, Any]] = [] for spec in ANCILLARY_ITEMS: raw = given.get(spec["key"]) try: amount = float(raw) if raw is not None and str(raw).strip() != "" else None except (TypeError, ValueError): amount = None reasons = [spec["why"]] if spec["code"] is None: reasons.append(REASON_NO_WORK_ITEM) if amount is None: reasons.append("개소가 아직 입력되지 않았습니다 — 넣으면 물량이 섭니다") status = STATUS_PENDING else: status = STATUS_READY if spec["code"] else STATUS_PENDING rows.append( { "group": "부대시설", "item": spec["item"], "unit": spec["unit"], "amount": amount, "status": status, "work_item_code": spec["code"], "legal_required": bool(spec["legal"]), "reason": " · ".join(reasons), } ) return rows