feat(B08): 준비공·사방공을 인계에 실음 — 못 내는 줄은 사유와 함께
보조 창 요청에서 출발했는데, 보다 보니 표가 통째로 안 가고 있었음. 표토제거(값 있음·FP-09-15)·규준틀(개소·FP-11-02)이 화면에는 서는데 인계에는 없었음. ⚠ 줄을 빼면 「빠졌다는 사실조차 안 보임」 — 받는 쪽에서 「내역서에 원래 없는 것」과 「우리가 아직 못 내는 것」이 구별되지 않음. - 값이 서는 줄 → in_bill: True, 공종코드와 수량 그대로 - 못 내는 줄 → in_bill: False + blocked_kind + blocked_reason (금액은 안 붙되 「무엇이 채워지면 풀리는지」가 함께 감) - ⚠ 「다른 표에서 이미 섬」(벌목)은 blocked_kind: None — 막힌 것이 아니라 여기서 세면 안 되는 줄임. 막힘으로 보내면 「만들어야 할 것」으로 오해됨 - origin: "preparation" 로 갈래를 나눔 시험 넷 — 값 서는 줄 · 못 내는 줄의 사유 · 이중계상 방지 줄은 막힘 아님 · ⚠ 표를 안 주면 빈 줄을 지어내지 않을 것(좁게 고치는 짝 시험). 시험: 681 passed · 24 skipped (B05 코리도 1건 기존 깨짐, 무관). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -45,6 +45,14 @@ from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Any, Iterable
|
||||
|
||||
from B08_Quantity.B08_Quantity_Engine_Preparation import (
|
||||
STATUS_COUNTED_ELSEWHERE as PREP_COUNTED_ELSEWHERE,
|
||||
)
|
||||
from B08_Quantity.B08_Quantity_Engine_Preparation import (
|
||||
STATUS_NOT_APPLICABLE as PREP_NOT_APPLICABLE,
|
||||
)
|
||||
from B08_Quantity.B08_Quantity_Engine_Preparation import STATUS_PENDING as PREP_PENDING
|
||||
from B08_Quantity.B08_Quantity_Engine_Preparation import STATUS_READY as PREP_READY
|
||||
from common_util.common_util_quantity_spread import spread_by_unit
|
||||
|
||||
DATASET_DIR = Path(__file__).resolve().parents[1] / "resources" / "data_work_item_mapping"
|
||||
@@ -149,6 +157,7 @@ ORIGIN_EARTHWORK = "earthwork"
|
||||
ORIGIN_STRUCTURE = "structure"
|
||||
ORIGIN_SLOPE = "slope"
|
||||
ORIGIN_HAUL = "haul"
|
||||
ORIGIN_PREPARATION = "preparation"
|
||||
|
||||
#: 암 시공법 → 매핑표의 지반 이름. 품셈이 **긁어내기와 터뜨리기를 다른 공종**으로 두기 때문에
|
||||
#: 갈래 이름(연암·보통암…)만으로는 공종을 못 고른다(2026-09-07 일감 9 실서버에서 드러남).
|
||||
@@ -777,6 +786,71 @@ def _placing_rows(
|
||||
return rows, notes
|
||||
|
||||
|
||||
def _preparation_rows(preparation_table: dict[str, Any]) -> list[dict[str, Any]]:
|
||||
"""준비공·사방공 줄 — **값이 서는 줄도, 못 서는 줄도** 함께 보낸다.
|
||||
|
||||
⚠ **줄을 빼면 「빠졌다는 사실조차 안 보인다」** (2026-09-08 보조 창 제보).
|
||||
받는 쪽 화면에서 「내역서에 원래 없는 것」과 「우리가 아직 못 내는 것」이 구별되지 않는다.
|
||||
그래서 못 내는 줄도 `in_bill: False` + `blocked_reason` 으로 실어 보낸다 —
|
||||
**금액은 안 붙되 「무엇이 채워지면 풀리는지」가 함께 간다.**
|
||||
|
||||
⚠ 이 표가 통째로 안 가고 있었다 — 표토제거(값 있음·`FP-09-15`)·규준틀(개소·`FP-11-02`)이
|
||||
화면에는 서는데 인계에는 없었다. 「사유를 실어 달라」는 요청을 보다 드러났다.
|
||||
"""
|
||||
rows: list[dict[str, Any]] = []
|
||||
for row in preparation_table.get("rows") or []:
|
||||
status = str(row.get("status") or "")
|
||||
amount = row.get("amount")
|
||||
ready = status == PREP_READY and amount is not None
|
||||
rows.append(
|
||||
{
|
||||
"work_item_code": row.get("work_item_code"),
|
||||
"name": str(row.get("item") or ""),
|
||||
"spec": str(row.get("group") or ""),
|
||||
"unit": str(row.get("unit") or ""),
|
||||
"quantity": float(amount or 0.0),
|
||||
"quantity_gross": None,
|
||||
"application_ratio_pct": None,
|
||||
"application_ratio_breakdown": None,
|
||||
"quantity_breakdown": None,
|
||||
"ground_class": None,
|
||||
"haul_distance_m": None,
|
||||
"haul_equipment": None,
|
||||
"station_from": None,
|
||||
"station_to": None,
|
||||
"excavation_method": None,
|
||||
"spec_detail": str(row.get("group") or ""),
|
||||
"composite_parts": None,
|
||||
"structure_kind": None,
|
||||
# ⚠ 못 서는 까닭을 그대로 넘긴다 — 받는 쪽이 「만들어야 할 것」 목록에 얹는다.
|
||||
"blocked_kind": None if ready else _prep_blocked_kind(status),
|
||||
"blocked_reason": "" if ready else str(row.get("reason") or status),
|
||||
"variant_axis": None,
|
||||
"variant_value": None,
|
||||
"spec_class": None,
|
||||
"spec_class_basis": str(row.get("reason") or ""),
|
||||
"composite_not_ready": None,
|
||||
# 값이 없는 줄은 **내역에 세우지 않는다** — 0 원 줄을 만들면 더 나쁘다.
|
||||
"in_bill": ready,
|
||||
"in_bill_reason": "" if ready else str(row.get("reason") or status),
|
||||
"origin": ORIGIN_PREPARATION,
|
||||
}
|
||||
)
|
||||
return rows
|
||||
|
||||
|
||||
def _prep_blocked_kind(status: str) -> str | None:
|
||||
"""준비공 줄의 상태를 **막힌 갈래 셋** 중 하나로 옮긴다. 모르는 상태는 `None`."""
|
||||
if status == PREP_PENDING:
|
||||
return BLOCKED_INPUT_MISSING
|
||||
if status == PREP_COUNTED_ELSEWHERE:
|
||||
# 다른 표에서 이미 선 줄 — 막힌 것이 아니라 **여기서 세면 안 되는** 줄이다.
|
||||
return None
|
||||
if status == PREP_NOT_APPLICABLE:
|
||||
return None
|
||||
return BLOCKED_UNIT_DATA_MISSING
|
||||
|
||||
|
||||
def _material_rows(material_table: dict[str, Any]) -> list[dict[str, Any]]:
|
||||
"""자재 줄 — **공종코드를 붙이지 않는다.** 자재 축은 B09 카탈로그가 잇는다(8-7)."""
|
||||
rows: list[dict[str, Any]] = []
|
||||
@@ -804,6 +878,7 @@ def build_handoff(
|
||||
haul_table: dict[str, Any] | None = None,
|
||||
unit_quantity_table: dict[str, Any] | None = None,
|
||||
material_table: dict[str, Any] | None = None,
|
||||
preparation_table: dict[str, Any] | None = None,
|
||||
mapping: WorkItemMapping | None = None,
|
||||
ground_class_set: str | None = None,
|
||||
ground_classes: list[str] | None = None,
|
||||
@@ -829,6 +904,9 @@ def build_handoff(
|
||||
work_items.extend(rows)
|
||||
unmatched.extend(misses)
|
||||
|
||||
# 준비공·사방공 — **못 내는 줄도 사유와 함께** 보낸다(빼면 빠진 줄이 안 보인다).
|
||||
work_items.extend(_preparation_rows(preparation_table or {}))
|
||||
|
||||
# 콘크리트 타설 — 품은 이 줄, 재료는 자재 쪽. 겹치지 않는다(위 `_placing_rows` 주석).
|
||||
placing_rows, placing_notes = _placing_rows(
|
||||
unit_quantity_table or {}, table, concrete_placing_method
|
||||
|
||||
@@ -159,6 +159,8 @@ async def get_handoff(project_id: UUID) -> JSONResponse:
|
||||
haul_table=earthwork.get("haul"),
|
||||
unit_quantity_table=unit_table,
|
||||
material_table=material_table,
|
||||
# 준비공·사방공 — 값이 서는 줄도, 못 내는 줄도 함께 넘긴다(빼면 빠진 줄이 안 보임).
|
||||
preparation_table=earthwork.get("preparation"),
|
||||
ground_class_set=settings.get("rock_class_set"),
|
||||
ground_classes=rock_classes(settings),
|
||||
ground_methods={name: rock_method(settings, name) for name in rock_classes(settings)},
|
||||
|
||||
Reference in New Issue
Block a user