feat(B09): 작업조+시공량 표 읽기 — 유로폼 노무 성립
품셈에는 소요량 대신 **「작업조 몇 인이 하루 몇 ㎡」**로 주는 표가 11건 있음. 1단위당 품 = 인원 ÷ 시공량. 행-자원으로 읽으면 **인원 4를 소요량 4로** 오해해 35배 부풀어, 형태 판정보다 **먼저** 가름 (유로폼 12-38-3 은 마스터에서 `reference` 로 찍혀 형태 필터에 버려지고 있었음) - 유로폼 설치·해체: 형틀목공 4 ÷ 35 = 0.1143인/㎡, 보통인부 1 ÷ 35 = 0.0286인/㎡ → 유형별 갈래로 세움 (복잡 51,009.1 · 보통 36,435.0 · 간단 31,880.7 원/㎡) - 유형은 우리가 안 고름 — 품셈 12-38-3 [유형] 이 「보통: 측구·수로·옹벽」으로 정해 두었으므로 갈래로 세우고 고르는 것은 B08 (철근 12-3 [주]① 과 같은 모양) - ⚠ 못 푼 작업조 줄이 있으면 **표째 버림** — 평떼 시비에서 「트럭 2.5ton 1대」가 조용히 빠지고 노무만으로 28.6원/㎡ 이 서 있었음(분포 최소값이 233 → 28.5 로 떨어진 것으로 발견) - 이름 안에 공백이 든 줄(「비 계 공」)은 한 이름으로 먼저 시도 — 공백을 구분자로만 보면 그런 표가 통째로 버려짐 결과: 자원 축 246 → 256, 일위대가 139 → 145, 최소값 233.4 유지 검증: pytest 181 통과(신규 6 — 손계산 대조 + 오탐 짝 시험 포함) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
"""B09 원가계산 — **작업조 + 시공량** 표 읽기 (2026-09-08).
|
||||
|
||||
품셈에는 소요량을 직접 안 주고 **「작업조 몇 인이 하루 몇 ㎡」** 로 주는 표가 있다.
|
||||
|
||||
구 분 | 단 위 | 수 량 | 시 공 량 (㎡)
|
||||
| | | 복잡 보통 간단
|
||||
형틀목공 | 인 | 4 | 25 35 40
|
||||
보통인부 | 인 | 1 |
|
||||
|
||||
1단위당 품 = 인원 ÷ 시공량 (유로폼 12-38-3: 형틀목공 4 ÷ 35 = 0.1143 인/㎡)
|
||||
|
||||
**유형은 우리가 안 고른다.** 품셈 12-38-3 [유형] 표가 「보통 : 측구, 수로, 옹벽 …」로
|
||||
정해 두었으므로 **갈래(`#복잡`·`#보통`·`#간단`)로 세워 두고 고르는 것은 B08**에 맡긴다
|
||||
(철근 12-3 [주]① 과 같은 모양).
|
||||
|
||||
⚠ **뭉쳐 온 칸을 짝지어 읽는다.** 이름·단위·인원이 한 칸에 붙어 온다
|
||||
(`형틀목공 보통인부` | `인 인` | `4 1`). **개수가 안 맞으면 그 표를 통째로 버린다** —
|
||||
자리를 밀어 읽으면 다른 직종의 품이 붙는다(오늘 여러 번 겪은 자리).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
from decimal import Decimal
|
||||
from typing import Any
|
||||
|
||||
from B09_Estimation.B09_Estimation_ResourceAxis import (
|
||||
AxisResult,
|
||||
ResourceCatalog,
|
||||
ResourceRow,
|
||||
UnmatchedRow,
|
||||
parse_amount,
|
||||
split_name_and_spec,
|
||||
)
|
||||
|
||||
_RE_NUMBER = re.compile(r"^\d+(?:,\d{3})*(?:\.\d+)?$")
|
||||
#: 시공량 열을 알아보는 말. 「시 공 량 (㎡)」처럼 띄어쓰기가 섞여 온다.
|
||||
_OUTPUT_WORDS = ("시공량", "기준시공량", "적용시공량")
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CrewMember:
|
||||
"""작업조 한 사람(또는 한 대)."""
|
||||
|
||||
code: str
|
||||
name: str
|
||||
spec: str
|
||||
kind: str
|
||||
count: Decimal
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CrewTable:
|
||||
"""작업조 표 한 장. `outputs` 는 유형별 시공량 — 갈래가 하나면 이름이 빈 문자열."""
|
||||
|
||||
members: tuple[CrewMember, ...]
|
||||
outputs: tuple[tuple[str, Decimal], ...]
|
||||
|
||||
def amount_of(self, member: CrewMember, output: Decimal) -> Decimal:
|
||||
"""1단위당 품 = 인원 ÷ 시공량."""
|
||||
return member.count / output
|
||||
|
||||
|
||||
def _normalize(text: str) -> str:
|
||||
return " ".join(str(text).split())
|
||||
|
||||
|
||||
def _is_output_header(cell: str) -> bool:
|
||||
tight = "".join(str(cell).split())
|
||||
return any(word in tight for word in _OUTPUT_WORDS)
|
||||
|
||||
|
||||
def _numbers_in(cell: str) -> list[Decimal]:
|
||||
"""한 칸에 뭉쳐 온 수들 — 「4 1」 → [4, 1]."""
|
||||
found: list[Decimal] = []
|
||||
for token in _normalize(cell).split(" "):
|
||||
if _RE_NUMBER.match(token):
|
||||
found.append(Decimal(token.replace(",", "")))
|
||||
return found
|
||||
|
||||
|
||||
def output_columns(table: dict[str, Any]) -> list[int]:
|
||||
"""시공량 열의 번호. 없으면 빈 목록 — 작업조 표가 아니다."""
|
||||
headers = table.get("condition_note") or []
|
||||
return [index for index, cell in enumerate(headers) if _is_output_header(cell)]
|
||||
|
||||
|
||||
def parse_crew_table(
|
||||
table: dict[str, Any],
|
||||
catalog: ResourceCatalog,
|
||||
) -> CrewTable | str:
|
||||
"""작업조 표를 읽는다. 못 읽으면 **사유 문자열**을 돌려준다(빈 표를 만들지 않는다)."""
|
||||
if not output_columns(table):
|
||||
return "시공량 열이 없습니다"
|
||||
|
||||
rows = [[str(c).strip() for c in row] for row in (table.get("raw_row") or [])]
|
||||
if not rows:
|
||||
return "빈 표입니다"
|
||||
|
||||
members: list[CrewMember] = []
|
||||
outputs: list[Decimal] = []
|
||||
labels: list[str] = []
|
||||
|
||||
for row in rows:
|
||||
if not row or not row[0]:
|
||||
continue
|
||||
head = row[0]
|
||||
# 첫 칸에 자원 이름이 뭉쳐 올 수 있다 — 「형틀목공 보통인부」.
|
||||
# ⚠ 이름 안에 공백이 든 표가 있다 — 「조 경 공」·「비 계 공」. 공백을 구분자로만
|
||||
# 보면 그런 줄이 통째로 안 풀린다. **한 이름으로 먼저 시도**하고, 안 되면 쪼갠다.
|
||||
tight = "".join(_normalize(head).split(" "))
|
||||
single = _resolve(catalog, tight)
|
||||
if single is not None:
|
||||
names, resolved = [tight], [single]
|
||||
else:
|
||||
names = [part for part in _normalize(head).split(" ") if part]
|
||||
resolved = [_resolve(catalog, name) for name in names]
|
||||
if not all(resolved):
|
||||
# 자원이 아니면 유형 라벨 줄로 본다 — 「복 잡 | 보 통 | 간 단」.
|
||||
if not _numbers_in(" ".join(row)):
|
||||
if not members:
|
||||
labels = [_normalize(cell) for cell in row if _normalize(cell)]
|
||||
continue
|
||||
# ⚠ **수가 있는데 이름을 못 푼 줄은 작업조의 한 몫**이다 — 조용히 건너뛰면
|
||||
# 그 몫이 빠진 채 단가가 선다(2026-09-08: 평떼 시비에서 「트럭 2.5ton 1대」가
|
||||
# 빠지고 노무만으로 28.6원/㎡ 이 섰다). 표를 통째로 버린다.
|
||||
return f"작업조 줄 「{_normalize(head)[:20]}」을 못 풀었습니다"
|
||||
|
||||
counts = _counts_of(row, len(resolved))
|
||||
if counts is None:
|
||||
return f"인원 수가 이름 {len(resolved)} 개와 안 맞습니다"
|
||||
for entry, count in zip(resolved, counts):
|
||||
members.append(
|
||||
CrewMember(
|
||||
code=entry.code,
|
||||
name=entry.name,
|
||||
spec=entry.spec,
|
||||
kind=entry.kind,
|
||||
count=count,
|
||||
)
|
||||
)
|
||||
# 시공량은 보통 **첫 작업조 줄**에 붙어 온다.
|
||||
if not outputs:
|
||||
outputs = _outputs_of(row, counts)
|
||||
|
||||
if not members:
|
||||
return "작업조 줄을 못 찾았습니다"
|
||||
if not outputs:
|
||||
return "시공량 값을 못 찾았습니다"
|
||||
if labels and len(labels) != len(outputs):
|
||||
# 라벨과 값의 개수가 다르면 **어느 유형인지 단정할 수 없다** — 읽지 않는다.
|
||||
return f"유형 {len(labels)} 개와 시공량 {len(outputs)} 개가 안 맞습니다"
|
||||
|
||||
named = tuple((labels[index] if labels else "", value) for index, value in enumerate(outputs))
|
||||
return CrewTable(members=tuple(members), outputs=named)
|
||||
|
||||
|
||||
def _resolve(catalog: ResourceCatalog, name_cell: str):
|
||||
name, spec = split_name_and_spec(name_cell)
|
||||
entry = catalog.resolve(name, spec)
|
||||
if entry is not None:
|
||||
return entry
|
||||
if spec:
|
||||
return None
|
||||
found = catalog.by_name(name)
|
||||
return found[0] if len(found) == 1 else None
|
||||
|
||||
|
||||
def _counts_of(row: list[str], wanted: int) -> list[Decimal] | None:
|
||||
"""이름 개수와 같은 만큼의 인원 수를 가진 칸을 찾는다. 없으면 `None`."""
|
||||
for cell in row[1:]:
|
||||
numbers = _numbers_in(cell)
|
||||
if len(numbers) == wanted:
|
||||
return numbers
|
||||
return None
|
||||
|
||||
|
||||
def _outputs_of(row: list[str], counts: list[Decimal]) -> list[Decimal]:
|
||||
"""인원 칸 **뒤**에 오는 수들이 시공량이다."""
|
||||
seen_counts = False
|
||||
values: list[Decimal] = []
|
||||
for cell in row[1:]:
|
||||
numbers = _numbers_in(cell)
|
||||
if not numbers:
|
||||
continue
|
||||
if not seen_counts and numbers == counts:
|
||||
seen_counts = True
|
||||
continue
|
||||
if seen_counts:
|
||||
values.extend(numbers)
|
||||
return values
|
||||
|
||||
|
||||
def match_crew_table(
|
||||
node: dict[str, Any],
|
||||
table: dict[str, Any],
|
||||
catalog: ResourceCatalog,
|
||||
result: AxisResult,
|
||||
unit: str,
|
||||
) -> bool:
|
||||
"""작업조 표를 자원 축 줄로 바꾼다. 그런 표가 아니면 `False`."""
|
||||
if not output_columns(table):
|
||||
return False
|
||||
|
||||
work_item_code = node.get("work_item_code", "")
|
||||
table_id = str(table.get("pum_table_id", ""))
|
||||
form = str(table.get("pum_form", ""))
|
||||
parsed = parse_crew_table(table, catalog)
|
||||
if isinstance(parsed, str):
|
||||
result.unmatched.append(
|
||||
UnmatchedRow(
|
||||
work_item_code=work_item_code,
|
||||
pum_table_id=table_id,
|
||||
cell=_normalize(" | ".join(str(c) for c in (table.get("condition_note") or []))),
|
||||
reason=f"작업조 표를 못 읽었습니다 — {parsed}",
|
||||
)
|
||||
)
|
||||
return True # 다른 길로 보내지 않는다 — 행-자원으로 읽으면 인원을 소요량으로 오해한다
|
||||
|
||||
for index, (label, output) in enumerate(parsed.outputs):
|
||||
if output <= 0:
|
||||
continue
|
||||
for member in parsed.members:
|
||||
result.rows.append(
|
||||
ResourceRow(
|
||||
work_item_code=work_item_code,
|
||||
pum_table_id=table_id,
|
||||
pum_form=form,
|
||||
resource_kind=member.kind,
|
||||
resource_code=member.code,
|
||||
resource_name=member.name,
|
||||
resource_spec=member.spec,
|
||||
amount=parsed.amount_of(member, output),
|
||||
amount_unit=unit,
|
||||
raw_row_index=index,
|
||||
variant=label,
|
||||
)
|
||||
)
|
||||
return True
|
||||
@@ -472,6 +472,15 @@ def match_table(
|
||||
result: AxisResult,
|
||||
) -> None:
|
||||
"""표 하나에 자원 축을 붙인다. 값이 안 서면 `unmatched` 로 보낸다."""
|
||||
from B09_Estimation.B09_Estimation_CrewOutput import match_crew_table
|
||||
|
||||
# ⚠ **작업조 표는 형태 판정보다 먼저 가른다.** 「형틀목공 4인 / 시공량 35㎡」 표는
|
||||
# 마스터에서 `reference` 로 찍혀 형태 필터에 먼저 걸려 버려지고 있었다(유로폼 12-38-3).
|
||||
# 그 표는 모양이 스스로를 말한다 — 시공량 열 + 작업조 줄이 있으면 그것이다.
|
||||
# 행-자원으로 읽으면 **인원 4를 소요량 4로** 오해해 35배 부푼다.
|
||||
if match_crew_table(node, table, catalog, result, table.get("basis_unit") or ""):
|
||||
return
|
||||
|
||||
form = table.get("pum_form", "")
|
||||
if form in NON_WORK_ITEM_FORMS or form in UNUSABLE_FORMS or form not in USABLE_FORMS:
|
||||
result.skipped_forms[form] = result.skipped_forms.get(form, 0) + 1
|
||||
|
||||
@@ -553,34 +553,6 @@
|
||||
"variant": "",
|
||||
"work_item_code": "FP-05-22-03"
|
||||
},
|
||||
{
|
||||
"amount": "2",
|
||||
"amount_unit": "",
|
||||
"group_ratio_pct": null,
|
||||
"pum_form": "requirement",
|
||||
"pum_table_id": "F0132",
|
||||
"raw_row_index": 0,
|
||||
"resource_code": "1038",
|
||||
"resource_kind": "labor",
|
||||
"resource_name": "조경공",
|
||||
"resource_spec": "",
|
||||
"variant": "",
|
||||
"work_item_code": "FP-05-22-04"
|
||||
},
|
||||
{
|
||||
"amount": "1",
|
||||
"amount_unit": "",
|
||||
"group_ratio_pct": null,
|
||||
"pum_form": "requirement",
|
||||
"pum_table_id": "F0132",
|
||||
"raw_row_index": 1,
|
||||
"resource_code": "1002",
|
||||
"resource_kind": "labor",
|
||||
"resource_name": "보통인부",
|
||||
"resource_spec": "",
|
||||
"variant": "",
|
||||
"work_item_code": "FP-05-22-04"
|
||||
},
|
||||
{
|
||||
"amount": "0.0084",
|
||||
"amount_unit": "㎡",
|
||||
@@ -763,6 +735,62 @@
|
||||
"variant": "",
|
||||
"work_item_code": "FP-05-28-01"
|
||||
},
|
||||
{
|
||||
"amount": "0.02222222222222222222222222222",
|
||||
"amount_unit": "",
|
||||
"group_ratio_pct": null,
|
||||
"pum_form": "reference",
|
||||
"pum_table_id": "F0145",
|
||||
"raw_row_index": 0,
|
||||
"resource_code": "1038",
|
||||
"resource_kind": "labor",
|
||||
"resource_name": "조경공",
|
||||
"resource_spec": "",
|
||||
"variant": "폭 1.5m 이하",
|
||||
"work_item_code": "FP-05-28-02"
|
||||
},
|
||||
{
|
||||
"amount": "0.01111111111111111111111111111",
|
||||
"amount_unit": "",
|
||||
"group_ratio_pct": null,
|
||||
"pum_form": "reference",
|
||||
"pum_table_id": "F0145",
|
||||
"raw_row_index": 0,
|
||||
"resource_code": "1002",
|
||||
"resource_kind": "labor",
|
||||
"resource_name": "보통인부",
|
||||
"resource_spec": "",
|
||||
"variant": "폭 1.5m 이하",
|
||||
"work_item_code": "FP-05-28-02"
|
||||
},
|
||||
{
|
||||
"amount": "0.01538461538461538461538461538",
|
||||
"amount_unit": "",
|
||||
"group_ratio_pct": null,
|
||||
"pum_form": "reference",
|
||||
"pum_table_id": "F0145",
|
||||
"raw_row_index": 1,
|
||||
"resource_code": "1038",
|
||||
"resource_kind": "labor",
|
||||
"resource_name": "조경공",
|
||||
"resource_spec": "",
|
||||
"variant": "폭 2.0m 이하",
|
||||
"work_item_code": "FP-05-28-02"
|
||||
},
|
||||
{
|
||||
"amount": "0.007692307692307692307692307692",
|
||||
"amount_unit": "",
|
||||
"group_ratio_pct": null,
|
||||
"pum_form": "reference",
|
||||
"pum_table_id": "F0145",
|
||||
"raw_row_index": 1,
|
||||
"resource_code": "1002",
|
||||
"resource_kind": "labor",
|
||||
"resource_name": "보통인부",
|
||||
"resource_spec": "",
|
||||
"variant": "폭 2.0m 이하",
|
||||
"work_item_code": "FP-05-28-02"
|
||||
},
|
||||
{
|
||||
"amount": "3.00",
|
||||
"amount_unit": "",
|
||||
@@ -2023,6 +2051,34 @@
|
||||
"variant": "소형구조물",
|
||||
"work_item_code": "FP-12-01-03"
|
||||
},
|
||||
{
|
||||
"amount": "0.009090909090909090909090909091",
|
||||
"amount_unit": "",
|
||||
"group_ratio_pct": null,
|
||||
"pum_form": "reference",
|
||||
"pum_table_id": "F0362",
|
||||
"raw_row_index": 0,
|
||||
"resource_code": "1006",
|
||||
"resource_kind": "labor",
|
||||
"resource_name": "비계공",
|
||||
"resource_spec": "",
|
||||
"variant": "설 치",
|
||||
"work_item_code": "FP-12-02"
|
||||
},
|
||||
{
|
||||
"amount": "0.006060606060606060606060606061",
|
||||
"amount_unit": "",
|
||||
"group_ratio_pct": null,
|
||||
"pum_form": "reference",
|
||||
"pum_table_id": "F0362",
|
||||
"raw_row_index": 1,
|
||||
"resource_code": "1006",
|
||||
"resource_kind": "labor",
|
||||
"resource_name": "비계공",
|
||||
"resource_spec": "",
|
||||
"variant": "철 거",
|
||||
"work_item_code": "FP-12-02"
|
||||
},
|
||||
{
|
||||
"amount": "1.07",
|
||||
"amount_unit": "ton",
|
||||
@@ -2304,35 +2360,7 @@
|
||||
"work_item_code": "FP-12-05"
|
||||
},
|
||||
{
|
||||
"amount": "1",
|
||||
"amount_unit": "",
|
||||
"group_ratio_pct": null,
|
||||
"pum_form": "requirement",
|
||||
"pum_table_id": "F0339",
|
||||
"raw_row_index": 1,
|
||||
"resource_code": "1003",
|
||||
"resource_kind": "labor",
|
||||
"resource_name": "특별인부",
|
||||
"resource_spec": "",
|
||||
"variant": "",
|
||||
"work_item_code": "FP-12-07-01"
|
||||
},
|
||||
{
|
||||
"amount": "2",
|
||||
"amount_unit": "",
|
||||
"group_ratio_pct": null,
|
||||
"pum_form": "requirement",
|
||||
"pum_table_id": "F0339",
|
||||
"raw_row_index": 2,
|
||||
"resource_code": "1002",
|
||||
"resource_kind": "labor",
|
||||
"resource_name": "보통인부",
|
||||
"resource_spec": "",
|
||||
"variant": "",
|
||||
"work_item_code": "FP-12-07-01"
|
||||
},
|
||||
{
|
||||
"amount": "2",
|
||||
"amount": "0.002857142857142857142857142857",
|
||||
"amount_unit": "",
|
||||
"group_ratio_pct": null,
|
||||
"pum_form": "requirement",
|
||||
@@ -2346,12 +2374,12 @@
|
||||
"work_item_code": "FP-12-07-02"
|
||||
},
|
||||
{
|
||||
"amount": "3",
|
||||
"amount": "0.004285714285714285714285714286",
|
||||
"amount_unit": "",
|
||||
"group_ratio_pct": null,
|
||||
"pum_form": "requirement",
|
||||
"pum_table_id": "F0340",
|
||||
"raw_row_index": 1,
|
||||
"raw_row_index": 0,
|
||||
"resource_code": "1002",
|
||||
"resource_kind": "labor",
|
||||
"resource_name": "보통인부",
|
||||
@@ -2779,6 +2807,90 @@
|
||||
"variant": "",
|
||||
"work_item_code": "FP-12-35"
|
||||
},
|
||||
{
|
||||
"amount": "0.16",
|
||||
"amount_unit": "",
|
||||
"group_ratio_pct": null,
|
||||
"pum_form": "reference",
|
||||
"pum_table_id": "F0395",
|
||||
"raw_row_index": 0,
|
||||
"resource_code": "1007",
|
||||
"resource_kind": "labor",
|
||||
"resource_name": "형틀목공",
|
||||
"resource_spec": "",
|
||||
"variant": "복 잡",
|
||||
"work_item_code": "FP-12-38-03"
|
||||
},
|
||||
{
|
||||
"amount": "0.04",
|
||||
"amount_unit": "",
|
||||
"group_ratio_pct": null,
|
||||
"pum_form": "reference",
|
||||
"pum_table_id": "F0395",
|
||||
"raw_row_index": 0,
|
||||
"resource_code": "1002",
|
||||
"resource_kind": "labor",
|
||||
"resource_name": "보통인부",
|
||||
"resource_spec": "",
|
||||
"variant": "복 잡",
|
||||
"work_item_code": "FP-12-38-03"
|
||||
},
|
||||
{
|
||||
"amount": "0.1142857142857142857142857143",
|
||||
"amount_unit": "",
|
||||
"group_ratio_pct": null,
|
||||
"pum_form": "reference",
|
||||
"pum_table_id": "F0395",
|
||||
"raw_row_index": 1,
|
||||
"resource_code": "1007",
|
||||
"resource_kind": "labor",
|
||||
"resource_name": "형틀목공",
|
||||
"resource_spec": "",
|
||||
"variant": "보 통",
|
||||
"work_item_code": "FP-12-38-03"
|
||||
},
|
||||
{
|
||||
"amount": "0.02857142857142857142857142857",
|
||||
"amount_unit": "",
|
||||
"group_ratio_pct": null,
|
||||
"pum_form": "reference",
|
||||
"pum_table_id": "F0395",
|
||||
"raw_row_index": 1,
|
||||
"resource_code": "1002",
|
||||
"resource_kind": "labor",
|
||||
"resource_name": "보통인부",
|
||||
"resource_spec": "",
|
||||
"variant": "보 통",
|
||||
"work_item_code": "FP-12-38-03"
|
||||
},
|
||||
{
|
||||
"amount": "0.1",
|
||||
"amount_unit": "",
|
||||
"group_ratio_pct": null,
|
||||
"pum_form": "reference",
|
||||
"pum_table_id": "F0395",
|
||||
"raw_row_index": 2,
|
||||
"resource_code": "1007",
|
||||
"resource_kind": "labor",
|
||||
"resource_name": "형틀목공",
|
||||
"resource_spec": "",
|
||||
"variant": "간 단",
|
||||
"work_item_code": "FP-12-38-03"
|
||||
},
|
||||
{
|
||||
"amount": "0.025",
|
||||
"amount_unit": "",
|
||||
"group_ratio_pct": null,
|
||||
"pum_form": "reference",
|
||||
"pum_table_id": "F0395",
|
||||
"raw_row_index": 2,
|
||||
"resource_code": "1002",
|
||||
"resource_kind": "labor",
|
||||
"resource_name": "보통인부",
|
||||
"resource_spec": "",
|
||||
"variant": "간 단",
|
||||
"work_item_code": "FP-12-38-03"
|
||||
},
|
||||
{
|
||||
"amount": "0.5",
|
||||
"amount_unit": "㎥",
|
||||
@@ -3422,6 +3534,62 @@
|
||||
"resource_spec": "",
|
||||
"variant": "",
|
||||
"work_item_code": "FP-13-15-01"
|
||||
},
|
||||
{
|
||||
"amount": "0.02222222222222222222222222222",
|
||||
"amount_unit": "",
|
||||
"group_ratio_pct": null,
|
||||
"pum_form": "reference",
|
||||
"pum_table_id": "F0449",
|
||||
"raw_row_index": 0,
|
||||
"resource_code": "1038",
|
||||
"resource_kind": "labor",
|
||||
"resource_name": "조경공",
|
||||
"resource_spec": "",
|
||||
"variant": "폭 1.5m 이하",
|
||||
"work_item_code": "FP-13-16-02"
|
||||
},
|
||||
{
|
||||
"amount": "0.01111111111111111111111111111",
|
||||
"amount_unit": "",
|
||||
"group_ratio_pct": null,
|
||||
"pum_form": "reference",
|
||||
"pum_table_id": "F0449",
|
||||
"raw_row_index": 0,
|
||||
"resource_code": "1002",
|
||||
"resource_kind": "labor",
|
||||
"resource_name": "보통인부",
|
||||
"resource_spec": "",
|
||||
"variant": "폭 1.5m 이하",
|
||||
"work_item_code": "FP-13-16-02"
|
||||
},
|
||||
{
|
||||
"amount": "0.01538461538461538461538461538",
|
||||
"amount_unit": "",
|
||||
"group_ratio_pct": null,
|
||||
"pum_form": "reference",
|
||||
"pum_table_id": "F0449",
|
||||
"raw_row_index": 1,
|
||||
"resource_code": "1038",
|
||||
"resource_kind": "labor",
|
||||
"resource_name": "조경공",
|
||||
"resource_spec": "",
|
||||
"variant": "폭 2.0m 이하",
|
||||
"work_item_code": "FP-13-16-02"
|
||||
},
|
||||
{
|
||||
"amount": "0.007692307692307692307692307692",
|
||||
"amount_unit": "",
|
||||
"group_ratio_pct": null,
|
||||
"pum_form": "reference",
|
||||
"pum_table_id": "F0449",
|
||||
"raw_row_index": 1,
|
||||
"resource_code": "1002",
|
||||
"resource_kind": "labor",
|
||||
"resource_name": "보통인부",
|
||||
"resource_spec": "",
|
||||
"variant": "폭 2.0m 이하",
|
||||
"work_item_code": "FP-13-16-02"
|
||||
}
|
||||
],
|
||||
"schema_version": "1.0",
|
||||
@@ -3436,12 +3604,12 @@
|
||||
"sha256": "fe454c56c9dc01ad7dae04a8f90d776d08c5ce33badeadb2606b35234bfce7eb"
|
||||
},
|
||||
"stats": {
|
||||
"rows": 244,
|
||||
"rows": 256,
|
||||
"skipped_forms": {
|
||||
"coefficient": 19,
|
||||
"reference": 98,
|
||||
"undetermined": 76
|
||||
"reference": 92,
|
||||
"undetermined": 75
|
||||
},
|
||||
"unmatched": 312
|
||||
"unmatched": 316
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,12 @@
|
||||
"effective_date": "2026-01-01",
|
||||
"note": "못 맞춘 자원 이름. 빈칸으로 두지 않고 여기 모은다. 기계·자재 카탈로그가 아직 없어 그 계열은 전부 여기로 온다.",
|
||||
"rows": [
|
||||
{
|
||||
"cell": "구분 | 조 건 | 적용시공량",
|
||||
"pum_table_id": "F0038",
|
||||
"reason": "작업조 표를 못 읽었습니다 — 작업조 줄 「1」을 못 풀었습니다",
|
||||
"work_item_code": "FP-01-04-26"
|
||||
},
|
||||
{
|
||||
"cell": "보통휘발유 (주연료)",
|
||||
"pum_table_id": "F0042",
|
||||
@@ -417,9 +423,9 @@
|
||||
"work_item_code": "FP-05-20"
|
||||
},
|
||||
{
|
||||
"cell": "트럭",
|
||||
"cell": "구 분 | 규 격 | 단위 | 수량 | 시공량(㎡)",
|
||||
"pum_table_id": "F0132",
|
||||
"reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)",
|
||||
"reason": "작업조 표를 못 읽었습니다 — 작업조 줄 「트럭」을 못 풀었습니다",
|
||||
"work_item_code": "FP-05-22-04"
|
||||
},
|
||||
{
|
||||
@@ -1370,6 +1376,12 @@
|
||||
"reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)",
|
||||
"work_item_code": "FP-10-10-02"
|
||||
},
|
||||
{
|
||||
"cell": "슬 럼 프 | 기준 시공량",
|
||||
"pum_table_id": "F0356",
|
||||
"reason": "작업조 표를 못 읽었습니다 — 작업조 줄 「8 ~ 12 cm」을 못 풀었습니다",
|
||||
"work_item_code": "FP-12-02"
|
||||
},
|
||||
{
|
||||
"cell": "합 판",
|
||||
"pum_table_id": "F0336",
|
||||
@@ -1407,9 +1419,21 @@
|
||||
"work_item_code": "FP-12-05"
|
||||
},
|
||||
{
|
||||
"cell": "형틀목공 보통인부",
|
||||
"cell": "배치인원(인) | 포장두께 | 시공량(㎥)",
|
||||
"pum_table_id": "F0338",
|
||||
"reason": "작업조 표를 못 읽었습니다 — 작업조 줄 「30㎝」을 못 풀었습니다",
|
||||
"work_item_code": "FP-12-06"
|
||||
},
|
||||
{
|
||||
"cell": "배치인원(인) | 사용기계(1대) | 시공량(m)",
|
||||
"pum_table_id": "F0339",
|
||||
"reason": "작업조 표를 못 읽었습니다 — 유형 4 개와 시공량 1 개가 안 맞습니다",
|
||||
"work_item_code": "FP-12-07-01"
|
||||
},
|
||||
{
|
||||
"cell": "배치인원(인) | 시공량(거푸집연장 m)",
|
||||
"pum_table_id": "F0341",
|
||||
"reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)",
|
||||
"reason": "작업조 표를 못 읽었습니다 — 작업조 줄 「20㎝ ≤ 포장두께 ≤ 25㎝」을 못 풀었습니다",
|
||||
"work_item_code": "FP-12-08"
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user