Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1
This commit is contained in:
@@ -24,6 +24,9 @@ from B08_Quantity.B08_Quantity_Engine_Preparation_Status import (
|
||||
from B08_Quantity.B08_Quantity_Engine_Preparation_Status import (
|
||||
STATUS_NEEDS_INPUT as PREP_NEEDS_INPUT,
|
||||
)
|
||||
from B08_Quantity.B08_Quantity_Engine_Preparation_Status import (
|
||||
STATUS_NO_WORK_ITEM as PREP_NO_WORK_ITEM,
|
||||
)
|
||||
from B08_Quantity.B08_Quantity_Engine_Preparation_Status import (
|
||||
STATUS_NOT_APPLICABLE as PREP_NOT_APPLICABLE,
|
||||
)
|
||||
@@ -82,9 +85,7 @@ def _preparation_rows(preparation_table: dict[str, Any]) -> list[dict[str, Any]]
|
||||
"composite_parts": None,
|
||||
"structure_kind": None,
|
||||
# ⚠ 못 서는 까닭을 그대로 넘긴다 — 받는 쪽이 「만들어야 할 것」 목록에 얹는다.
|
||||
"blocked_kind": None
|
||||
if ready
|
||||
else row.get("blocked_kind") or _prep_blocked_kind(status),
|
||||
"blocked_kind": None if ready else _prep_blocked_kind(status),
|
||||
"blocked_reason": "" if ready else str(row.get("reason") or status),
|
||||
# ⚠ 준비공 줄도 갈래를 실어 보낸다(2026-09-09) — 종전에는 늘 `None` 이라
|
||||
# 표토 운반처럼 **부모 공종코드**로 가는 줄이 B09 에서 「후보 N건」에 머물렀다.
|
||||
@@ -110,7 +111,7 @@ def _preparation_rows(preparation_table: dict[str, Any]) -> list[dict[str, Any]]
|
||||
|
||||
|
||||
def _prep_blocked_kind(status: str) -> str | None:
|
||||
"""준비공 줄의 상태를 **막힌 갈래 셋** 중 하나로 옮긴다. 줄이 갈래를 적어 오면 그것이 이긴다.
|
||||
"""준비공 줄의 상태를 **막힌 갈래 셋** 중 하나로 옮긴다. 모르는 상태는 단가 자료 없음.
|
||||
|
||||
⚠ 「근거 없음」을 `input_missing` 으로 보내면 사방 원단위처럼 **우리가 만들 줄**이 B09 에
|
||||
「입력이 필요합니다」로 뜬다 — 사용자가 넣을 칸을 찾아 헤맨다(2026-09-14 브레인 ㉴).
|
||||
@@ -122,7 +123,8 @@ def _prep_blocked_kind(status: str) -> str | None:
|
||||
if status == PREP_COUNTED_ELSEWHERE:
|
||||
# 다른 표에서 이미 선 줄 — 막힌 것이 아니라 **여기서 세면 안 되는** 줄이다.
|
||||
return None
|
||||
if status == PREP_NOT_APPLICABLE:
|
||||
if status in (PREP_NOT_APPLICABLE, PREP_NO_WORK_ITEM):
|
||||
# 공종 없는 줄은 자재총괄 줄로 금액이 섬(「자재 단가」 탭) — 여기서 막힘으로 세면 두 벌.
|
||||
return None
|
||||
return BLOCKED_UNIT_DATA_MISSING
|
||||
|
||||
|
||||
@@ -12,10 +12,13 @@ from typing import Any
|
||||
from B08_Quantity.B08_Quantity_Engine_Preparation_Status import (
|
||||
REASON_NO_WORK_ITEM,
|
||||
STATUS_NEEDS_INPUT,
|
||||
STATUS_PENDING,
|
||||
STATUS_NO_WORK_ITEM,
|
||||
STATUS_READY,
|
||||
)
|
||||
|
||||
#: 공종 없는 줄에 개소가 들어온 뒤 할 일 — 자재총괄(사급) 줄로 가서 「자재 단가」 탭에 칸이 섬.
|
||||
NO_WORK_ITEM_PRICE_PATH = "「자재 단가」 탭에 단가를 넣으면 자재총괄(사급) 줄로 금액이 섬"
|
||||
|
||||
#: 부대시설·가설공사 — **법이 요구하는데 우리가 안 내던 다섯 줄**(2026-09-09 사용자 확정 ⑬).
|
||||
#: `key` 는 설정의 `ancillary_counts` 칸 이름, `code` 는 품셈 공종(없으면 `None`).
|
||||
#: ⚠ 다섯 중 **품셈에 공종이 있는 것은 가설창고 하나뿐**이다(마스터 전수 확인).
|
||||
@@ -96,8 +99,11 @@ def ancillary_rows(counts: dict[str, Any] | None = None) -> list[dict[str, Any]]
|
||||
if amount is None:
|
||||
reasons.append("개소가 아직 입력되지 않았습니다 — 넣으면 물량이 섭니다")
|
||||
status = STATUS_NEEDS_INPUT
|
||||
elif spec["code"]:
|
||||
status = STATUS_READY
|
||||
else:
|
||||
status = STATUS_READY if spec["code"] else STATUS_PENDING
|
||||
reasons.append(NO_WORK_ITEM_PRICE_PATH)
|
||||
status = STATUS_NO_WORK_ITEM
|
||||
rows.append(
|
||||
{
|
||||
"group": "부대시설",
|
||||
@@ -107,9 +113,26 @@ def ancillary_rows(counts: dict[str, Any] | None = None) -> list[dict[str, Any]]
|
||||
"status": status,
|
||||
"work_item_code": spec["code"],
|
||||
"legal_required": bool(spec["legal"]),
|
||||
# 품셈에 공종이 없는 줄 — 금액은 별도 단가(사람 입력)로만 서서 인계는 늘 입력 갈래.
|
||||
**({} if spec["code"] else {"blocked_kind": "input_missing"}),
|
||||
"reason": " · ".join(reasons),
|
||||
}
|
||||
)
|
||||
return rows
|
||||
|
||||
|
||||
def ancillary_material_rows(preparation_rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
"""공종 없는 부대시설 중 개소가 선 줄 → 자재총괄 성분(사급 기본) — 「자재 단가」 탭 통로.
|
||||
|
||||
⚠ 인계 공종 줄은 막힘 없이 `in_bill: False` 로 가므로 **여기 한 곳에서만** 금액이 선다.
|
||||
"""
|
||||
return [
|
||||
{
|
||||
"name": row["item"],
|
||||
"spec": "",
|
||||
"unit": row["unit"],
|
||||
"amount": float(row["amount"]),
|
||||
"destination": "material",
|
||||
"source": str(row.get("group") or "부대시설"),
|
||||
}
|
||||
for row in preparation_rows
|
||||
if row.get("status") == STATUS_NO_WORK_ITEM and row.get("amount")
|
||||
]
|
||||
|
||||
@@ -15,6 +15,10 @@ STATUS_READY = "값 있음"
|
||||
STATUS_NEEDS_INPUT = "입력이 필요함"
|
||||
#: 자료·산식이 우리에게 없는 줄 — 입력으로는 안 풀림. 인계 `unit_data_missing`.
|
||||
STATUS_PENDING = "값을 낼 근거가 없음"
|
||||
#: 수량은 섰는데 **품셈에 공종이 없어** 단가가 영영 안 서는 줄 — 「자재 단가」 탭에 단가를 넣음
|
||||
#: (화약류·치즐과 같은 통로 · 자재총괄 사급 줄로 감). 인계는 막힘이 아님(자재 줄로 셈).
|
||||
#: ⚠ 「입력이 필요함」으로 두면 개소를 넣고 기다려도 안 섬(2026-09-14 브레인 판정).
|
||||
STATUS_NO_WORK_ITEM = "품셈 공종 없음 — 단가를 직접 넣어야 함"
|
||||
STATUS_COUNTED_ELSEWHERE = "다른 표에서 이미 섬"
|
||||
STATUS_NOT_APPLICABLE = "해당 없음"
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ from common_util.common_util_project_settings import (
|
||||
)
|
||||
from B08_Quantity.B08_Quantity_Engine_HaulInputs import haul_inputs
|
||||
from B08_Quantity.B08_Quantity_Engine_Preparation import frame_material_rows
|
||||
from B08_Quantity.B08_Quantity_Engine_Preparation_Ancillary import ancillary_material_rows
|
||||
from common_util.common_util_storage import resolve_stored_project_path
|
||||
from common_util.common_util_structure_lengths import structure_lengths
|
||||
from config.config_db import run_with_connection
|
||||
@@ -172,7 +173,9 @@ def material_table_for(
|
||||
surcharge_overrides=settings.get("material_surcharge") or {},
|
||||
# 콘크리트 할증은 **레미콘일 때만** 붙는다 — 방식이 이름을 가른다(확정 3차 ⑥).
|
||||
concrete_placing_method=settings.get("concrete_placing_method"),
|
||||
extra_materials=frame_material_rows(frame_rows, settings.get("frame_material") or {}),
|
||||
extra_materials=frame_material_rows(frame_rows, settings.get("frame_material") or {})
|
||||
# 공종 없는 부대시설 개소 — 「자재 단가」 탭에 단가를 넣는 통로(2026-09-14 브레인 판정).
|
||||
+ ancillary_material_rows((preparation_table or {}).get("rows") or []),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -87,14 +87,44 @@ def test_인계_막힘_갈래가_상태를_따른다() -> None:
|
||||
assert handed["골막이"]["blocked_kind"] == "unit_data_missing"
|
||||
# 해당 없음은 막힘이 아니다.
|
||||
assert handed["비탈 규준틀"]["blocked_kind"] is None
|
||||
# 공종이 품셈에 없는 부대시설은 개소를 넣어도 종전대로 「입력」(별도 단가) 갈래.
|
||||
entered = _table(ancillary_counts={"national_point_sign": 3})
|
||||
# 개소를 안 넣은 부대시설은 입력 갈래(개소를 넣으면 수량이 섬).
|
||||
assert handed["국가지점번호판"]["blocked_kind"] == "input_missing"
|
||||
|
||||
|
||||
def test_품셈_공종이_없는_부대시설은_셋째_상태로_자재_단가에_잇는다() -> None:
|
||||
"""개소를 넣어도 공종이 없어 단가가 영영 안 선다 — 「입력이 필요함」으로 두면 사용자가 기다린다.
|
||||
|
||||
브레인 판정(2026-09-14): 「품셈 공종 없음 — 단가를 직접 넣어야 함」 · 「자재 단가」 탭 통로
|
||||
(화약류·치즐과 같은 자리) — 수량은 자재총괄(사급) 줄로 가고 단가를 넣으면 금액이 선다.
|
||||
"""
|
||||
from B08_Quantity.B08_Quantity_Engine_Preparation_Status import STATUS_NO_WORK_ITEM
|
||||
from B08_Quantity.B08_Quantity_Router_Material import material_table_for
|
||||
|
||||
entered = _table(ancillary_counts={"national_point_sign": 3, "site_container": 1})
|
||||
rows = _by_item(entered)
|
||||
assert rows["국가지점번호판"]["status"] == STATUS_NO_WORK_ITEM
|
||||
assert STATUS_NO_WORK_ITEM == "품셈 공종 없음 — 단가를 직접 넣어야 함"
|
||||
assert "자재 단가" in rows["국가지점번호판"]["reason"]
|
||||
# 공종이 있는 가설창고는 그대로 값 있음.
|
||||
assert rows["가설창고(컨테이너)"]["status"] == "값 있음"
|
||||
# 인계 — 막힘이 아니라 자재 줄로 가는 줄(두 번 세지 않음).
|
||||
sign = next(
|
||||
row
|
||||
for row in build_handoff(preparation_table=entered)["work_items"]
|
||||
if row["name"] == "국가지점번호판"
|
||||
)
|
||||
assert sign["blocked_kind"] == "input_missing"
|
||||
assert sign["blocked_kind"] is None and sign["in_bill"] is False
|
||||
# 자재총괄 사급 줄 — 「자재 단가」 탭이 이 이름으로 칸을 세운다.
|
||||
materials = material_table_for({"rows": []}, {}, entered)["rows"]
|
||||
board = [row for row in materials if row["name"] == "국가지점번호판"]
|
||||
assert len(board) == 1
|
||||
assert (board[0]["unit"], board[0]["net_amount"], board[0]["supply"]) == (
|
||||
"개소",
|
||||
3.0,
|
||||
"contractor_supplied",
|
||||
)
|
||||
# 개소를 안 넣은 공종 없는 줄은 자재로 안 감(수량이 없음).
|
||||
assert not [row for row in materials if row["name"] == "임도 안내판"]
|
||||
|
||||
|
||||
def test_표_머리가_두_갈래를_따로_센다() -> None:
|
||||
|
||||
Reference in New Issue
Block a user