Merge remote-tracking branch 'origin/main_desktop_1' into sub_desktop_1

This commit is contained in:
2026-09-08 08:25:33 +09:00
2 changed files with 80 additions and 0 deletions
@@ -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)},