feat(B09): 제잡비를 경비로 붙임 + ㉥ 물빼기 파이프 가드 + 성분 빠짐 차단 확대
제잡비 (품셈 13-6-1·13-7-1 [주]③, 원문은 메인이 찾아 줌) - **노무비 합계 × 비율이 경비(J)로** 붙음. 재료·노무가 아님 - 기본은 **아랫단**(물빼기 파이프 미설치). 「상한」이라 산출근거에 그 사실을 적음 - `PriceDetail.percent_of_labor` 신설 — 밑수가 3분할 전체인 `percent_of_parent` 와 다름. 비율 줄은 참조를 풀기 전에 처리(자기 자신을 가리켜 순환으로 잡혔음) - 표시 합계에도 넣음 — 안 넣으면 화면 합계와 실제 단가가 어긋남 ㉥ 가드 신설 — 제잡비 윗단(파이프 설치)을 쓰면서 물구멍을 자재로 또 실으면 멈춤 (원문이 「상단에는 파이프 설치의 노무비·재료비를 포함한다」고 명시). 짝 시험 4건 — 아랫단·파이프 없음·비슷한 이름은 안 걸림 성분 빠짐 차단 확대 - 시간·대수로 세는 줄(장비 몫)을 못 맞추면 그 공종 금액을 안 만듦. 초본류 시비가 「트럭(2.5t) 2.6시간」을 빼고 33.1원/㎡ 로 서 있었음 - 자재 줄(kg·매)은 이미 알려진 미결이라 막지 않고 드러내기만 함 - 요약의 「의심 저가」에서 막아 둔 공종은 제외 — 새로 살펴야 할 것과 섞이지 않게 뭉친 표 두 곳 수정 - 칸 전체를 한 이름으로 먼저 시도 — 「굴착기 (무한궤도)」가 안 풀려 찰쌓기 13-6-2 의 장비 몫이 빠지고 공종이 막혀 있었음 - ⚠ 라벨 줄 판정에 **순수 숫자 칸** 검사 추가 — 「자재 | 종자 | kg | 0.025」를 라벨 줄로 오해해 씨앗뿜어붙이기 표를 가로챘음(회귀, 시험이 잡음) 결과: 자원 축 354 → 418, 일위대가 176 → 203, 막힌 공종 32 찰쌓기 13-6-2 = 118,794.9원/㎡ (노무 + 굴착기 0.48h + 제잡비 3 %) 검증: pytest 190 통과(신규 6) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -455,6 +455,9 @@ class AxisResult:
|
||||
rows: list[ResourceRow] = field(default_factory=list)
|
||||
unmatched: list[UnmatchedRow] = field(default_factory=list)
|
||||
skipped_forms: dict[str, int] = field(default_factory=dict)
|
||||
#: 제잡비 비율(%) — `{공종코드: (윗단, 아랫단)}`. 윗단은 물빼기 파이프 설치,
|
||||
#: 아랫단은 미설치 (품셈 13-6-2 [주]③). 값이 하나뿐이면 둘이 같다.
|
||||
overhead_ratio: dict = field(default_factory=dict)
|
||||
#: 자원은 알아봤는데 **값을 못 읽은** 줄이 있는 공종 — 그 단가는 「일부만 선 것」이다.
|
||||
#: 기초잡석 12-25 가 `소할(30%) | 할석공(인) | 0.2 × 30%` 를 못 읽어 부설다짐만으로
|
||||
#: 107,145 원이 서고 있었다(2026-09-08). **부분 성공이 가장 위험하다.**
|
||||
@@ -481,6 +484,19 @@ def _tidy_resource_name(cell: str) -> str:
|
||||
return tight + sep + tail
|
||||
|
||||
|
||||
#: 장비 줄의 단위 — 시간·대수로 센다. 자재는 kg·매·㎥ 로 센다.
|
||||
_MACHINE_UNITS = ("시간", "hr", "h", "대", "시 간")
|
||||
|
||||
|
||||
def _is_machine_like_row(cells: list[str]) -> bool:
|
||||
"""그 줄이 **장비 몫**인가 — 단위 칸이 시간·대수인지로 본다."""
|
||||
for cell in cells:
|
||||
text = _normalize(cell)
|
||||
if text and any(text == unit or text.replace(" ", "") == unit for unit in _MACHINE_UNITS):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _resolve_cell(catalog: ResourceCatalog, name_cell: str, cells: list[str]):
|
||||
"""셀 하나를 카탈로그 한 줄로 푼다 — 세 가지 모양을 차례로 시도한다.
|
||||
|
||||
@@ -579,6 +595,20 @@ def match_table(
|
||||
name_cell = cells[1]
|
||||
value_cells = cells[2:]
|
||||
|
||||
# 제잡비 비율 줄 — 자원이 아니라 **노무비에 붙는 경비율**이다(품셈 [주]③).
|
||||
if "제잡비" in _normalize(name_cell):
|
||||
ratios = [
|
||||
parse_amount(_normalize(token))
|
||||
for cell in value_cells
|
||||
for token in _RE_ALTERNATIVE.sub(r"", _normalize(cell)).split(" ")
|
||||
]
|
||||
ratios = [x for x in ratios if x is not None]
|
||||
if ratios:
|
||||
upper = ratios[0]
|
||||
lower = ratios[1] if len(ratios) > 1 else ratios[0]
|
||||
result.overhead_ratio[node["work_item_code"]] = (upper, lower)
|
||||
continue
|
||||
|
||||
# ⚠ **공식 계수를 단 줄은 자원 줄이 아니다.** 「유압식백호우 … | k | 0.9」 처럼
|
||||
# 같은 줄에 버킷계수가 붙어 오는데, 그 0.9 를 소요량으로 읽으면 **시간당 사용료가
|
||||
# 0.9시간분** 붙어 이중이 된다(2026-09-08: 이름 표기를 맞추자 측구터파기에
|
||||
@@ -650,6 +680,14 @@ def match_table(
|
||||
result.unmatched.append(
|
||||
UnmatchedRow(node["work_item_code"], table["pum_table_id"], name_cell, reason)
|
||||
)
|
||||
# ⚠ **시간·대수로 세는 줄은 장비 몫**이다 — 못 맞추면 그 공종 단가가
|
||||
# 노무만으로 서서 조용히 싸진다(2026-09-08: 초본류 시비가 「트럭(2.5t)
|
||||
# 2.6시간」을 빼고 33.1원/㎡ 로 섰다). 자재 줄(kg·매)은 이미 알려진
|
||||
# 미결이라 막지 않고 드러내기만 한다.
|
||||
if _is_machine_like_row(value_cells):
|
||||
result.partial_items[node["work_item_code"]] = (
|
||||
f"{_normalize(name_cell)[:20]} (장비 줄)을 못 맞췄습니다"
|
||||
)
|
||||
continue
|
||||
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user