Files
Aislo/B08_Quantity/B08_Quantity_Engine_Preparation_Ancillary.py
T
eomsangdonandClaude Opus 5 5b97f280ba fix(B08): 표토 원문 대조 셋 + 준비공 파일 700줄 분리
원문·실무 대조에서 나온 것:
- 품셈 9-15-2(답외구간) 원문에 T(표토두께) 0.2m · L(운반거리) 20m 가 적용값으로
  박혀 있음. 종전 사유 「품셈이 정하는 값이 아닙니다」가 반쪽이라 정정 — 값을
  자동으로 넣지는 않되 원문에 있는 값을 없다고 말하지도 않음.
- 그 품에 20m 압토가 이미 들어 있음. 실무 내역서(영월 2024)도 「표토제거 답외구간
  / M2 · 도자 19Ton · D=20」 한 줄뿐이고 운반 줄이 따로 없음. 우리 운반 줄은
  20m 를 넘는 몫일 때만 새 줄인데 그 가름을 아직 안 함 — 사유로 남김.
- FP-10-12(덤프 운반)은 부모 코드라 품이 없음. 잎은 토사/암절취/발파암이고
  갈래를 안 보내면 B09 가 후보만 보이고 금액이 안 섬. 표토는 토사이므로
  variant_axis=ground_class · variant_value=토사 로 실어 보냄. 준비공 줄이
  갈래를 넘길 통로도 함께 엶(종전에는 늘 None).

파일 분리(700줄 제한):
- 부대시설 다섯 줄을 _Preparation_Ancillary 로, 상태 낱말을 _Preparation_Status 로
  가름. 상태 문자열은 받는 쪽 판정에 쓰이므로 두 벌이 되지 않게 한 벌만 둠.
  710줄 → 622 + 113 + 16.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 12:58:02 +09:00

113 lines
4.5 KiB
Python

"""준비공 — 부대시설·가설공사 다섯 줄.
⚠ `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