feat(B09): 기계만 쓰는 공종이 공식만으로 서게 함 — 층따기 살아남
「미판정 77건」을 훑다 나온 것 — 그중 일부는 **미판정이 아니라 시공능력 공식표**였음 ``` F0287 9-18 층따기 굴착기 0.7㎥ · K 0.9 · f 1/1.3 · E 0.7 · Cm 22 F0328 11-4 쇄석·혼합석 부설 같은 모양 ``` - ⚠ **자원 줄이 하나도 없으면 조립 반복문이 그 공종을 아예 안 보고 있었음** — 표에 인력 줄이 없는 기계 전용 공종이 그 자리. 공식이 온전하면 세우게 고침 - 층따기 **1,550.9원/㎡** · 쇄석 부설 **3,549.5원/㎡** 로 섬 (일위대가 203 → 206) - 내역서(2f940d8a) 합계 **2,149,464 → 6,252,172원** — 층따기 2,645.21㎡ × 1,550.9 = 4,102,708 이 실제로 붙음 - ⚠ 짝 시험 — 자원도 공식도 없으면 **여전히 안 섬**(0 원 일위대가 금지). 그 문이 넓으면 빈 제목이 무더기로 서고 화면이 「단가가 있다」고 거짓말함 검증: pytest 215 통과(신규 2) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -333,6 +333,19 @@ def build_unit_prices(axis: AxisResult | None = None) -> UnitPriceBuild:
|
||||
for row in axis.rows:
|
||||
by_item.setdefault((row.work_item_code, getattr(row, "variant", "")), []).append(row)
|
||||
|
||||
# ⚠ **자원 줄이 하나도 없어도 공식이 온전하면 세운다.** 기계만 쓰는 공종(층따기 9-18 ·
|
||||
# 쇄석 부설 11-4)은 표에 인력 줄이 없어 자원 축이 비고, 그러면 아래 반복문이 그 공종을
|
||||
# 아예 안 본다 — 「미판정」으로 남아 있던 것이 실은 **시공능력 공식표**였다
|
||||
# (2026-09-08 미판정 77건을 훑다 발견).
|
||||
for node in master.get("work_items", []):
|
||||
code = node.get("work_item_code")
|
||||
if not code or (code, "") in by_item:
|
||||
continue
|
||||
for table in node.get("tables", []):
|
||||
if isinstance(extract_cycle_factors(code, table), CycleFactors):
|
||||
by_item[(code, "")] = []
|
||||
break
|
||||
|
||||
for (work_item_code, variant), rows in sorted(by_item.items()):
|
||||
# 갈래 키는 **내부 공백을 지운 것**, 화면 문구는 **원문 그대로**
|
||||
# (2026-09-08 두 창 합의). 원문이 「보 통」·「보 통」으로 들쭉날쭉해
|
||||
@@ -349,7 +362,8 @@ def build_unit_prices(axis: AxisResult | None = None) -> UnitPriceBuild:
|
||||
for row in rows
|
||||
]
|
||||
attachable = [(row, ref) for row, ref in attachable if ref in build.book.titles]
|
||||
if not attachable:
|
||||
if not attachable and not _has_full_formula(master, work_item_code):
|
||||
# 붙을 상세도 없고 공식도 없으면 **제목도 안 세운다**(0 원 일위대가 금지).
|
||||
build.skipped.append(work_item_code)
|
||||
continue
|
||||
|
||||
@@ -416,6 +430,20 @@ def build_unit_prices(axis: AxisResult | None = None) -> UnitPriceBuild:
|
||||
return build
|
||||
|
||||
|
||||
def _has_full_formula(master: dict, work_item_code: str) -> bool:
|
||||
"""그 공종에 **온전한 시공능력 공식**이 있는가 (기계만 쓰는 공종용)."""
|
||||
node = next(
|
||||
(w for w in master.get("work_items", []) if w.get("work_item_code") == work_item_code),
|
||||
None,
|
||||
)
|
||||
if node is None:
|
||||
return False
|
||||
return any(
|
||||
isinstance(extract_cycle_factors(work_item_code, table), CycleFactors)
|
||||
for table in node.get("tables", [])
|
||||
)
|
||||
|
||||
|
||||
def _share_of(row) -> Decimal:
|
||||
"""그 줄이 차지하는 몫(0~1). 배분율이 없으면 1 — 종전과 같다."""
|
||||
ratio = getattr(row, "group_ratio_pct", None)
|
||||
|
||||
Reference in New Issue
Block a user