Merge remote-tracking branch 'origin/dev' into sub_laptop_1

This commit is contained in:
2026-09-13 21:57:21 +09:00
7 changed files with 253 additions and 28 deletions
@@ -35,6 +35,8 @@ from B09_Estimation.B09_Estimation_ResourceAxis import (
)
_RE_NUMBER = re.compile(r"^\d+(?:,\d{3})*(?:\.\d+)?$")
#: 두께 갈래 칸 — 「20㎝」. 넓히면 규격 칸(「0.6㎥」)을 갈래로 오해한다.
_RE_THICKNESS = re.compile(r"^\d+(?:\.\d+)?\s*(?:㎝|cm)$")
#: 시공량 열을 알아보는 말. 「시 공 량 (㎡)」처럼 띄어쓰기가 섞여 온다.
_OUTPUT_WORDS = ("시공량", "기준시공량", "적용시공량")
@@ -101,6 +103,7 @@ def parse_crew_table(
members: list[CrewMember] = []
outputs: list[Decimal] = []
labels: list[str] = []
thickness: list[tuple[str, Decimal]] = []
for row in rows:
if not row or not row[0]:
@@ -117,6 +120,11 @@ def parse_crew_table(
names = [part for part in _normalize(head).split(" ") if part]
resolved = [_resolve(catalog, name) for name in names]
if not all(resolved):
# 두께 갈래 줄 — 「30㎝ | 150」. 콘크리트 포장 12-6 은 두께 칸이 작업조 칸과
# **줄을 엇갈려** 병합돼 있어(hwpx 확인) 마스터가 작업조 줄 사이에 두께 줄을 끼워 둔다.
if _RE_THICKNESS.match(head) and len(_numbers_in(" ".join(row[1:]))) == 1:
thickness.append((head, _numbers_in(" ".join(row[1:]))[0]))
continue
# 자원이 아니면 유형 라벨 줄로 본다 — 「복 잡 | 보 통 | 간 단」.
if not _numbers_in(" ".join(row)):
if not members:
@@ -143,9 +151,17 @@ def parse_crew_table(
# 시공량은 보통 **첫 작업조 줄**에 붙어 온다.
if not outputs:
outputs = _outputs_of(row, counts)
# 작업조 줄에 두께 갈래가 붙어 온 자리 — 「포장공 | 3 | 20㎝ | 100 | …」.
for label, value in zip(row, row[1:]):
if _RE_THICKNESS.match(label) and _RE_NUMBER.match(value):
thickness.append((label, Decimal(value.replace(",", ""))))
if not members:
return "작업조 줄을 못 찾았습니다"
if thickness:
# 두께마다 시공량이 하나 — 그 옆 시공량 칸(「후진 진입·경운기 운반은 좌측의 50%까지 감」)은
# **범위라 값이 아니다.** 유형 라벨은 그 두 칸의 머리라 두께 갈래와 짝이 아니다.
return CrewTable(members=tuple(members), outputs=tuple(thickness))
if not outputs:
return "시공량 값을 못 찾았습니다"
if labels and len(labels) != len(outputs):
@@ -206,6 +222,16 @@ def match_crew_table(
work_item_code = node.get("work_item_code", "")
table_id = str(table.get("pum_table_id", ""))
form = str(table.get("pum_form", ""))
from B09_Estimation.B09_Estimation_WorkItemUnit import unit_of
if not unit and not unit_of(work_item_code):
# 밑수가 「일당」이라 비어 있는 표(12-6) — 1단위당 품의 단위는 **시공량 열 머리**의 단위다.
# ⚠ 공종 단위가 따로 적힌 자리는 그대로 둔다(12-2 에 딸린 비계 표가 m 를 들고 옴).
headers = table.get("condition_note") or []
found = [
re.search(r"[(]\s*([^)]+?)\s*[)]", str(headers[i])) for i in output_columns(table)
]
unit = found[0].group(1) if found and found[0] else ""
parsed = parse_crew_table(table, catalog)
if isinstance(parsed, str):
result.unmatched.append(
@@ -446,6 +446,13 @@ def match_table(
if match_process_sum_table(node, table, catalog, result, basis_quantity, unit):
return
# 머리가 두 단으로 병합된 표(인력 돌쌓기 13-4-1·13-4-4) — hwpx 병합으로 확인한 공종만.
from B09_Estimation.B09_Estimation_ResourceAxis_TwoLevelHeader import (
match_two_level_header_table,
)
if match_two_level_header_table(node, table, catalog, result, basis_quantity, unit):
return
from B09_Estimation.B09_Estimation_ResourceAxis_Transposed import match_transposed_table
@@ -0,0 +1,126 @@
"""B09 원가계산 — **머리가 두 단으로 병합된 표** 읽기 (자원 축 보조, 2026-09-13).
인력 돌쌓기 13-4-1 메쌓기 · 13-4-4 찰쌓기 — hwpx 원본의 병합 칸으로 머리 모양을 확인함.
뒷길이(㎝) | 견치돌(4칸) | 깬돌(4칸) | 깬잡석(4칸) | 호박돌 및 야면석(2칸·두 줄 병합)
| 골쌓기 켜쌓기 | 골쌓기 켜쌓기 | 골쌓기 켜쌓기 |
| 석공 | 보통인부 × 7 벌
35 | 0.50 | 0.40 | 0.55 | 0.44 | … | 0.16 | 0.14
마스터는 병합을 풀어 칸만 늘어놓아 **어느 값이 어느 돌·쌓기인지**가 사라졌다 — 행 읽기 길은
「값 묶음 0 개가 갈래 6 개와 안 맞습니다」로 표째 버렸다.
읽는 법 — 자원 줄의 되풀이 폭(석공·보통인부 = 2)으로 **잎 칸 수**(14 ÷ 2 = 7)를 얻고,
소분류 줄의 되풀이(골·켜 × 3)가 **앞의 대분류 셋**을 두 잎씩 먹고, 남은 대분류가 한 잎씩 먹는다.
⚠ 「앞에서부터」는 hwpx 병합으로 확인한 두 표에만 참이다 — **적어 둔 공종만** 읽고,
칸 수가 딱 맞지 않으면 한 줄도 안 세우고 막는다.
"""
from __future__ import annotations
from decimal import Decimal
from typing import Any
from B09_Estimation.B09_Estimation_ResourceAxis import (
AxisResult,
ResourceCatalog,
ResourceRow,
UnmatchedRow,
parse_amount,
)
#: 공종 → 확인 근거. 여기 없는 공종은 이 길로 안 읽는다.
TWO_LEVEL_HEADER: dict[str, str] = {
"FP-13-04-01": "hwpx 병합 — 견치돌·깬돌·깬잡석 4칸(골·켜) · 호박돌 및 야면석 2칸(행 병합)",
"FP-13-04-04": "hwpx 병합 — 견치돌·깬돌·깬잡석 4칸(골·켜) · 호박돌 및 야면석 2칸(행 병합)",
}
_ABSENT = ("-", "", "", "")
def _label(text: Any) -> str:
"""「견 치 돌」처럼 벌려 쓴 한 낱말은 붙이고, 「호박돌 및 야면석」은 그대로."""
parts = str(text or "").split()
return "".join(parts) if parts and all(len(p) == 1 for p in parts) else " ".join(parts)
def _period(items: list[str]) -> int:
"""되풀이 폭 — 딱 나뉘는 가장 짧은 주기. 없으면 전체 길이."""
for width in range(1, len(items) + 1):
if len(items) % width == 0 and items == items[:width] * (len(items) // width):
return width
return len(items)
def match_two_level_header_table(
node: dict[str, Any],
table: dict[str, Any],
catalog: ResourceCatalog,
result: AxisResult,
basis_quantity: Decimal | None,
unit: str,
) -> bool:
"""적어 둔 공종의 두 단 머리 표를 읽는다. 그 공종이 아니면 `False`."""
from B09_Estimation.B09_Estimation_CrewOutput import _resolve
from B09_Estimation.B09_Estimation_ResourceAxis_ThreeAxis import _axis_label
code = str(node.get("work_item_code") or "")
if code not in TWO_LEVEL_HEADER:
return False
table_id = str(table.get("pum_table_id", ""))
head = list(table.get("condition_note") or [])
rows = [[str(c).strip() for c in row] for row in (table.get("raw_row") or [])]
def block(why: str) -> bool:
result.unmatched.append(UnmatchedRow(code, table_id, " | ".join(head), why))
result.partial_items[code] = why
return True
names = [c for c in rows[1] if c] if len(rows) >= 3 else []
entries = [_resolve(catalog, "".join(name.split())) for name in names]
if len(head) < 3 or len(names) < 2 or not all(entries):
# 같은 절에 딸린 다른 표(뒷길이 표준·표준경사)다 — 원래 길로 보낸다.
return False
if basis_quantity in (None, 0):
return block("두 단 머리 표에 밑수가 없습니다")
groups = [_label(g) for g in head[1:]]
subs = [_label(c) for c in rows[0] if c]
width = _period([e.code for e in entries])
leaves = len(names) // width
cycle = _period(subs)
split = len(subs) // cycle
if split > len(groups) or split * cycle + (len(groups) - split) != leaves:
return block(f"대분류 {len(groups)} · 소분류 {len(subs)} · 잎 {leaves} 칸이 딱 안 나뉩니다")
leaf_labels = [[g, s] for g in groups[:split] for s in subs[:cycle]]
leaf_labels += [[g] for g in groups[split:]]
staged: list[ResourceRow] = []
for index, cells in enumerate(rows[2:], start=2):
values = [c for c in cells[1:] if c]
if not cells or not cells[0]:
continue
readable = all(v in _ABSENT or parse_amount(v) is not None for v in values)
if len(values) != len(names) or not readable:
return block(
f"뒷길이 {cells[0]} 줄의 값 {len(values)} 칸이 자원 {len(names)} 칸과 안 맞습니다"
)
for position, (value, entry) in enumerate(zip(values, entries)):
if value in _ABSENT:
continue # 그 돌·뒷길이엔 품이 없다
labels = leaf_labels[position // width]
staged.append(
ResourceRow(
work_item_code=code,
pum_table_id=table_id,
pum_form=str(table.get("pum_form", "")),
resource_kind=entry.kind,
resource_code=entry.code,
resource_name=entry.name,
resource_spec=entry.spec,
amount=parse_amount(value) / basis_quantity,
amount_unit=unit,
raw_row_index=index,
variant=" · ".join([*labels, _axis_label(_label(head[0]), cells[0])]),
)
)
result.rows.extend(staged)
return True
@@ -50,4 +50,4 @@
- 실무 레시피 350개 중 품셈 근거문구 보유는 82개뿐이다. 근거 없는 268개는 현행값으로 임의 확정하지 않는다.
- 과거 임도품셈 절 번호가 현행 산림사업 표준품셈에서 크게 바뀌었다. 번호만 같은 다른 절을 자동 연결하지 않는다.
- JSON의 `수량문자_확인`은 후보 절 본문에 같은 숫자가 있는지를 세는 1차 검색값이다. `일치` 확정은 위 두 항목처럼 조건과 행을 직접 대조한 경우만 한다.
- 후속 판 대응표는 `33_임도품셈_판_대응표.md``30_원자료/임도품셈_판_대응표.csv`에 작성했다. 확정 2·추정 11·미확인 1이며, 추정·미확인은 자동 치환에 쓰지 않는다.
- 후속 판 대응표는 `33_임도품셈_판_대응표.md``30_원자료/임도품셈_판_대응표.csv`에 작성했다. 2017·2018·2024 원문 대조 결과 확정 12·미확인 2이며, 미확인은 자동 치환에 쓰지 않는다.
@@ -1,28 +1,37 @@
# 과거 임도품셈 ↔ 2026 현행 산림사업 표준품셈 대응표
작성일: 2026-09-13
산출물: `30_원자료/임도품셈_판_대응표.csv`
현행 기준: 산림청고시 제2025-82호, 2026-01-01 시행
## 결과
STmate 레시피에 남은 임도계 품셈 인용 **14개를 전수 등록**했다.
STmate 레시피 임도계 품셈 인용 14개를 2017 임도표준품셈, 2018 통합 초판, 2024 개정판, 2026 현행판 순으로 대조했다.
| 판정 | 수 | 뜻 |
|---|---:|---|
| 확정 | 2 | 명칭·적용조건과 관측 수량을 현행 원문에서 직접 대조 |
| 추정 | 11 | 명칭·공종 맥락으로 현행 후보를 특정했으나 과거 원문판 또는 수량 대조가 부족 |
| 미확인 | 1 | 현행 후보가 작업방식까지 같다는 근거가 없음 |
| 확정 | 13 | 절 이동 경로와 표의 장비·계수·수량 또는 현행 원문의 적용 대상을 직접 대조 |
| 추정 | 0 | 원문 대조 후 남은 추정 없음 |
| 미확인 | 1 | 외부 건설품셈을 참조하여 현행 산림품셈 절로 연결 불가 |
확정은 `2-3 제근 → 9-21 제근`, STmate에서 `4-16-2`로 인용한 **절취없는 단끊기** `5-16-1 단끊기` 두 건이다. 후자는 보통인부 `0.34·0.36·1.17인/100m`가 현행 세 공정과 각각 일치하는 범위에서만 확정했다.
2018 통합 초판의 목차에는 2017 임도품셈 번호가 괄호로 직접 병기되어 있다. 이 표기로 `3-3-1→4.3.2`, `3-3-2→4.3.3`, `3-6→4.24`, `3-7→4.25`, `5-1-1→4.27.1`, `5-1-2→4.27.2`, `7-3→4.40`을 확인했다. 2024 개정판과 2026 현행판은 실제 표의 장비 규격, 작업능력, 계수와 수량까지 대조했다.
## 코드 사용 규칙
## 추가 확인 결과
- 대응표의 현행 절은 `work_item_master_2026-01-01.json``FP-*` 코드와 표 ID를 함께 기록했다.
- 과거 한 절이 현행 여러 절로 나뉜 후보는 슬래시로 모두 보존했다. 대표 코드 하나를 임의 선택하지 않는다.
- `추정``미확인`은 코드 자동 치환에 사용하지 않는다. 값도 옮기지 않고 확인 대기로 세운다.
- 과거 판본 원문이 확보되면 표 제목·주석·수량을 대조해 `추정 → 확정` 또는 `미확인`으로 갱신한다.
- `7-3 노면다짐`: 2017·2018 원문 모두 자체 품 없이 건설품셈 기계화 시공을 참조한다. 2024판 목차에는 노면다짐이 없고, 현행 `3-4-3`은 인력식 노면 보수이므로 대응 절을 `미확인`으로 유지한다.
- `9-3 임도성토면다짐`: 공종은 `2017 3-6 → 2018 4.24 → 2024 8-18 → 현행 9-17-1 비탈면 다짐`으로 이어진다. 현행 원문도 `임도성토면 다짐` 적용, 굴착기 부착용 유압식 진동 콤펙터와 굴착기 `0.7㎥`, `A=77.7㎡/시간`을 직접 명시하므로 절 대응은 `확정`한다. 다만 STmate의 `9-3``Q=716.8`은 원문과 불일치하므로 해당 번호·값은 복사하지 않는다.
## 경계
## 사용 경계
저장소에는 2026 현행판만 있고 STmate가 인용한 과거 임도품셈 원문판은 없다. 따라서 같은 이름이라는 이유만으로 확정하지 않았다. 특히 `7-3 노면정리`는 현행 `3-4-3`의 인력식 노면정지·노면굴기와 작업방식이 달라 현행 절을 `미확인`으로 남겼다.
- 확정 13건은 **절 번호 대응**이 확정된 것이다. STmate 레시피의 값 전체가 현행에 적합하다는 뜻은 아니다.
- 과거 한 절이 현행 여러 절로 분리된 `선떼붙이기공``암절취`는 일대다 대응을 유지한다. 대표 하위 절 하나로 자동 치환하지 않는다.
- `5-1-1 나 암절취`의 STmate 장비 규격 `굴삭기 0.4㎥`는 원문 `0.7㎥`와 다르므로 절 대응과 별개로 값 복사를 금지한다.
## 확인 원문
- 산림청, `임도표준품셈 최종파일 188264.pdf`(2017): 인쇄면 32, 38, 63~65, 91쪽
- 산림청, `2018년도 산림사업 표준품셈-최종.pdf`: 목차의 구절→신절 병기 및 4.16.2·4.16.4·4.39
- 산림청, `2024년도 개정판 안전 산림사업 표준품셈.pdf`: 인쇄면 60, 62, 146~148, 156~167, 188쪽
- 2026 현행: `resources/knowledge/original/행정규칙/임도 품셈 적용기준 (현 산림사업 표준품셈)/첨부/(산림청고시 제2025-82호) 산림사업 표준품셈.md` 9-17-1 및 `resources/data_work_item_master/work_item_master_2026-01-01.json`
@@ -1,15 +1,15 @@
과거판,과거_절,과거_인용명,STmate_적용공종,현행판,현행_절,현행_FP코드,현행_명칭,현행_표ID,판정,근거와_경계
"판본 미확인(STmate 2024~2025 출력)","번호 미기재","되메우기 및 다짐","구조물 되메우기(사질양토) 기계100%(굴삭기 0.7㎥)","산림청고시 2025-82호(2026-01-01)","9-14-1","FP-09-14-01","되메우기","F0279","","공종명과 적용맥락 일치. 과거 절 번호·원문 미확보"
"판본 미확인(STmate 2024~2025 출력)","5-1-2 사","암터파기","구조물터파기(육상암 0~1m) 기계100%(굴삭기 0.7㎥)","산림청고시 2025-82호(2026-01-01)","9-13-7","FP-09-13-07","육상 암절취(01m)","F0267","","구조물·육상암·0~1m 조건 일치. 수량 전수 대조 전"
"판본 미확인(STmate 2024~2025 출력)","7-3","노면정리(건설품셈 기계 시공 적용)","노면정리","산림청고시 2025-82호(2026-01-01)","미확인","미확인","미확인","미확인","미확인","현행 3-4-3은 노면정지·노면굴기 인력 보수표라 기계식 노면정리와 동일하다고 볼 수 없음"
"판본 미확인(STmate 2024~2025 출력)","4-16-2","선떼붙이기공","단끊기","산림청고시 2025-82호(2026-01-01)","5-16 / 5-16-1 / 5-16-2","FP-05-16 / FP-05-16-01 / FP-05-16-02","선떼붙이기공 / 단끊기 / 선떼붙이기","F0121 / F0122","","과거 한 절이 현행 부모와 두 하위 절로 분리된 후보. 단끊기 일부만 별도 확정"
"판본 미확인(STmate 2024~2025 출력)","4-16-2","절취없는 단끊기","단끊기 절취없음","산림청고시 2025-82호(2026-01-01)","5-16-1","FP-05-16-01","단끊기","F0121","확정","보통인부 0.34·0.36·1.17인/100m가 현행 세 공정과 각각 일치. 적용범위는 절취없는 구성에 한정"
"판본 미확인(STmate 2024~2025 출력)","3-6 가","임도성토면 다짐","성토사면다짐 B/H0.7+콤팩터","산림청고시 2025-82호(2026-01-01)","9-17-1","FP-09-17-01","비탈면 다짐","미확인","","성토사면과 현행 비탈면 다짐의 기능상 후보. 현행 마스터에 직접 표가 없어 수량 대조 필요"
"판본 미확인(STmate 2024~2025 출력)","9-3","임도성토면다짐","성토사면다짐 굴삭기0.7㎥","산림청고시 2025-82호(2026-01-01)","9-17-1","FP-09-17-01","비탈면 다짐","미확인","","같은 STmate 자료 안에서도 과거 절 인용이 3-6과 9-3으로 갈림. 과거 판본부터 확인 필요"
"판본 미확인(STmate 2024~2025 출력)","4-16-4","파종공","씨뿌리기(성토면) 줄파종","산림청고시 2025-82호(2026-01-01)","5-18","FP-05-18","씨뿌리기(줄)","F0125","","줄파종·씨뿌리기 명칭과 적용맥락 일치. 과거 원문·수량 미대조"
"판본 미확인(STmate 2024~2025 출력)","3-3-2","굴삭기 적용 암절취","암절취 굴삭기0.7㎥+대형브레카","산림청고시 2025-82호(2026-01-01)","9-4 / 9-4-1 / 9-4-2","FP-09-04 / FP-09-04-01 / FP-09-04-02","암절취 / 암파쇄 / 집토","F0241 / F0242","","현행은 암절취를 암파쇄+집토 단계합산형으로 분리. 과거 한 절과 일대다 후보"
"판본 미확인(STmate 2024~2025 출력)","2-3","제근(100㎡당)","제근","산림청고시 2025-82호(2026-01-01)","9-21","FP-09-21","제근","F0294","확정","밀림 보통인부 0.05인이 현행 9-21과 직접 일치"
"판본 미확인(STmate 2024~2025 출력)","5-1-1 나","암절취","측구터파기(암) 기계100%(굴삭기 0.4㎥)","산림청고시 2025-82호(2026-01-01)","9-12-2","FP-09-12-02","암절취","F0259","","측구터파기·암 조건으로 현행 절 후보 특정. 수량·장비조건 미대조"
"판본 미확인(STmate 2024~2025 출력)","3-7 가","층따기","층따기","산림청고시 2025-82호(2026-01-01)","9-18","FP-09-18","층따기","F0287","","명칭 직접 일치하지만 과거 원문판이 없어 절 동일성 확정 불가"
"판본 미확인(STmate 2024~2025 출력)","3-3-1","토사 굴삭기 적용","토사절취(흙깎기) 굴삭기0.7㎥","산림청고시 2025-82호(2026-01-01)","9-3-2","FP-09-03-02","기계","F0240","","현행 9-3 토사깍기의 기계 하위 절 후보. 수량·토질조건 미대조"
"판본 미확인(STmate 2024~2025 출력)","4-39","쇄석·혼합석 부설","혼합석부설 T=0.1m","산림청고시 2025-82호(2026-01-01)","11-4","FP-11-04","쇄석․혼합석 부설","F0328","","명칭 직접 일치하지만 두께·장비·수량 조건 미대조"
"STmate_인용판","과거_절","과거_인용명","STmate_적용공종","2017_임도품셈","2018_통합품셈","2024_개정품셈","현행_절","현행_FP코드","현행_명칭","현행_표ID","판정","근거와_경계"
"판본 미확인","번호 미기재","되메우기 및 다짐","구조물 되메우기(사질양토) 기계100%(굴삭기 0.7㎥)","미확인","미확인","8-13 되메우기 및 다짐","9-14-1","FP-09-14-01","되메우기","F0279","","2024 8-13과 현행 9-14-1의 굴삭기 0.7㎥·k=0.9·f=0.69·E=0.65·Cm=18(90°)가 동일. 인용 절·판본은 미확인이고 대응은 되메우기 구성에 한정"
"2017 임도표준품셈","5-1-2 사","암터파기","구조물터파기(육상암 0~1m) 기계100%(굴삭기 0.7㎥)","5-1-2 사 육상 암절취(0~1m)","4.27.2 구조물터파기","8-12 사 육상 암절취(0~1m)","9-13-7","FP-09-13-07","육상 암절취(01m)","F0267","","2018 목차에 5-1-2→4.27.2가 직접 병기되고 2024·현행의 인력10%+장비90%, 브레이커 Q=3.5, 치즐 0.006, 굴삭기 0.7㎥ 계수가 동일"
"2017 임도표준품셈","7-3","노면정리(건설품셈 기계 시공 적용)","노면정리","7-3 노면다짐","4.40 노면다짐","미확인","미확인","미확인","미확인","미확인","미확인","2017과 2018은 해당 절에서 건설품셈 기계화 시공을 외부 참조할 뿐 자체 표가 없음. 2024 목차에 노면다짐이 없고 현행 3-4-3은 인력식 노면 보수이므로 연결 금지"
"2018 통합 산림사업 표준품셈","4-16-2","선떼붙이기공","단끊기","선행 사방품셈 4-2-2","4.16.2 선떼붙이기공","4-16 선떼붙이기공","5-16 / 5-16-1 / 5-16-2","FP-05-16 / FP-05-16-01 / FP-05-16-02","선떼붙이기공 / 단끊기 / 선떼붙이기","F0121 / F0122","","2018 절과 2024 절이 직접 이어지고 현행은 단끊기·선떼붙이기로 분리. 일대다 대응으로 확정하며 대표 하위 절 하나로 자동 치환하지 않음"
"2018 통합 산림사업 표준품셈","4-16-2","절취없는 단끊기","단끊기 절취없음","선행 사방품셈 4-2-2","4.16.2 선떼붙이기공","4-16 가 단끊기","5-16-1","FP-05-16-01","단끊기","F0121","확정","2024와 현행의 수평잡기·단정리 0.34, 잡석·뿌리정리 0.36, 절·성토면고르기 1.17인/100m가 동일. 절취없는 구성에 한정"
"2017 임도표준품셈","3-6 가","임도성토면 다짐","성토사면다짐 B/H0.7+콤팩터","3-6 임도성토면 다짐","4.24 임도성토면 다짐","8-18 임도성토면 다짐","9-17-1","FP-09-17-01","비탈면 다짐","미확인","","2018 목차에 3-6→4.24가 직접 병기되고 2017·2024 모두 굴삭기 부착 유압식 진동 콤팩터+굴삭기 0.7㎥, A=77.7㎡/hr로 동일. 현행은 명칭을 비탈면 다짐으로 변경"
"판본·인용 오류 미확인","9-3","임도성토면다짐","성토사면다짐 굴삭기0.7㎥","3-6 임도성토면 다짐","4.24 임도성토면 다짐","8-18 임도성토면 다짐","9-17-1","FP-09-17-01","비탈면 다짐","미확인","","공종·장비는 2017 3-6→2018 4.24→2024 8-18→현행 9-17-1로 이어짐. 현행 원문이 임도성토면 다짐, 굴착기 부착 유압식 진동 콤펙터+굴착기 0.7㎥, A=77.7㎡/hr를 직접 명시함. STmate의 9-3과 Q=716.8은 원문과 불일치하므로 절 대응만 확정하고 해당 번호·값 복사는 금지"
"2018 통합 산림사업 표준품셈","4-16-4","파종공","씨뿌리기(성토면) 줄파종","선행 사방품셈 4-2-4","4.16.4 파종공","4-18 씨뿌리기(줄)","5-18","FP-05-18","씨뿌리기(줄)","F0125","","2024·현행의 종자 2g, 비료 29.3g, 특별인부 0.00055, 보통인부 0.00838인/m가 STmate 구성과 일치"
"2017 임도표준품셈","3-3-2","굴삭기 적용 암절취","암절취 굴삭기0.7㎥+대형브레카","3-3-2 암절취","4.3.3 암절취","8-3 암절취","9-4 / 9-4-1 / 9-4-2","FP-09-04 / FP-09-04-01 / FP-09-04-02","암절취 / 암파쇄 / 집토","F0241 / F0242","","2018 목차에 3-3-2→4.3.3이 직접 병기되고 2024·현행은 암파쇄+집토로 분리. 브레이커 작업능력 5.0·3.4·2.6㎥/hr와 굴삭기 0.7㎥ 계수가 동일한 일대다 대응"
"2017 임도표준품셈","2-3","제근(100㎡당)","제근","2-3 제근","4.20.2 제근","8-22 제근","9-21","FP-09-21","제근","F0294","확정","2018 목차에 2-3→4.20.2가 직접 병기되고 2024 8-22를 거쳐 현행 9-21로 이동. 밀림 보통인부 0.05인/100㎡ 일치"
"2017 임도표준품셈","5-1-1 나","암절취","측구터파기(암) 기계100%(굴삭기 0.4㎥)","5-1-1 나 암절취","4.27.1 나 암절취","8-11 나 암절취","9-12-2","FP-09-12-02","암절취","F0259","","2018 목차에 5-1-1→4.27.1이 직접 병기됨. 2024·현행의 연암 Q=3.5, 치즐 0.006 및 측구터파기 조건이 동일. STmate 장비 규격 0.4㎥ 표기는 원문 0.7㎥와 불일치하므로 값 복사는 금지"
"2017 임도표준품셈","3-7 가","층따기","층따기","3-7 층따기","4.25 층따기","8-19 층따기","9-18","FP-09-18","층따기","F0287","","2018 목차에 3-7→4.25가 직접 병기되고 2017·2024·현행 모두 굴삭기 0.7㎥, K=0.9, f=1/1.3, E=0.7, Cm=22(180°)가 동일"
"2017 임도표준품셈","3-3-1","토사 굴삭기 적용","토사절취(흙깎기) 굴삭기0.7㎥","3-3-1 토사","4.3.2 토사깎기","8-2 토사깎기","9-3-2","FP-09-03-02","기계","F0240","","2018 목차에 3-3-1→4.3.2가 직접 병기되고 2017·2024·현행의 굴삭기 0.7㎥, K=0.9, f=1/1.30, E=0.55~0.45, Cm=20(135°)가 동일"
"2018 통합 산림사업 표준품셈","4-39","쇄석·혼합석 부설","혼합석부설 T=0.1m","7-2 쇄석·혼합석 부설(선행)","4.39 쇄석·혼합석 부설","10-4 쇄석·혼합석 부설","11-4","FP-11-04","쇄석․혼합석 부설","F0328","","2018 목차에 7-2→4.39가 직접 병기됨. 2024·현행의 굴삭기 0.7㎥, q=0.7, k=0.55, f=1, E=0.35, Cm=20(135°)가 동일. T=0.1m 환산은 별도 계산"
1 과거판 STmate_인용판 과거_절 과거_인용명 STmate_적용공종 현행판 2017_임도품셈 2018_통합품셈 2024_개정품셈 현행_절 현행_FP코드 현행_명칭 현행_표ID 판정 근거와_경계
2 판본 미확인(STmate 2024~2025 출력) 판본 미확인 번호 미기재 되메우기 및 다짐 구조물 되메우기(사질양토) 기계100%(굴삭기 0.7㎥) 산림청고시 2025-82호(2026-01-01) 미확인 미확인 8-13 되메우기 및 다짐 9-14-1 FP-09-14-01 되메우기 F0279 추정 확정 공종명과 적용맥락 일치. 과거 절 번호·원문 미확보 2024 8-13과 현행 9-14-1의 굴삭기 0.7㎥·k=0.9·f=0.69·E=0.65·Cm=18(90°)가 동일. 인용 절·판본은 미확인이고 대응은 되메우기 구성에 한정
3 판본 미확인(STmate 2024~2025 출력) 2017 임도표준품셈 5-1-2 사 암터파기 구조물터파기(육상암 0~1m) 기계100%(굴삭기 0.7㎥) 산림청고시 2025-82호(2026-01-01) 5-1-2 사 육상 암절취(0~1m) 4.27.2 구조물터파기 8-12 사 육상 암절취(0~1m) 9-13-7 FP-09-13-07 육상 암절취(0~1m) F0267 추정 확정 구조물·육상암·0~1m 조건 일치. 수량 전수 대조 전 2018 목차에 5-1-2→4.27.2가 직접 병기되고 2024·현행의 인력10%+장비90%, 브레이커 Q=3.5, 치즐 0.006, 굴삭기 0.7㎥ 계수가 동일
4 판본 미확인(STmate 2024~2025 출력) 2017 임도표준품셈 7-3 노면정리(건설품셈 기계 시공 적용) 노면정리 산림청고시 2025-82호(2026-01-01) 7-3 노면다짐 4.40 노면다짐 미확인 미확인 미확인 미확인 미확인 미확인 현행 3-4-3은 노면정지·노면굴기 인력 보수표라 기계식 노면정리와 동일하다고 볼 수 없음 2017과 2018은 해당 절에서 건설품셈 기계화 시공을 외부 참조할 뿐 자체 표가 없음. 2024 목차에 노면다짐이 없고 현행 3-4-3은 인력식 노면 보수이므로 연결 금지
5 판본 미확인(STmate 2024~2025 출력) 2018 통합 산림사업 표준품셈 4-16-2 선떼붙이기공 단끊기 산림청고시 2025-82호(2026-01-01) 선행 사방품셈 4-2-2 4.16.2 선떼붙이기공 4-16 선떼붙이기공 5-16 / 5-16-1 / 5-16-2 FP-05-16 / FP-05-16-01 / FP-05-16-02 선떼붙이기공 / 단끊기 / 선떼붙이기 F0121 / F0122 추정 확정 과거 한 절이 현행 부모와 두 하위 절로 분리된 후보. 단끊기 일부만 별도 확정 2018 절과 2024 절이 직접 이어지고 현행은 단끊기·선떼붙이기로 분리. 일대다 대응으로 확정하며 대표 하위 절 하나로 자동 치환하지 않음
6 판본 미확인(STmate 2024~2025 출력) 2018 통합 산림사업 표준품셈 4-16-2 절취없는 단끊기 단끊기 절취없음 산림청고시 2025-82호(2026-01-01) 선행 사방품셈 4-2-2 4.16.2 선떼붙이기공 4-16 가 단끊기 5-16-1 FP-05-16-01 단끊기 F0121 확정 보통인부 0.34·0.36·1.17인/100m가 현행 세 공정과 각각 일치. 적용범위는 절취없는 구성에 한정 2024와 현행의 수평잡기·단정리 0.34, 잡석·뿌리정리 0.36, 절·성토면고르기 1.17인/100m가 동일. 절취없는 구성에 한정
7 판본 미확인(STmate 2024~2025 출력) 2017 임도표준품셈 3-6 가 임도성토면 다짐 성토사면다짐 B/H0.7+콤팩터 산림청고시 2025-82호(2026-01-01) 3-6 임도성토면 다짐 4.24 임도성토면 다짐 8-18 임도성토면 다짐 9-17-1 FP-09-17-01 비탈면 다짐 미확인 추정 확정 성토사면과 현행 비탈면 다짐의 기능상 후보. 현행 마스터에 직접 표가 없어 수량 대조 필요 2018 목차에 3-6→4.24가 직접 병기되고 2017·2024 모두 굴삭기 부착 유압식 진동 콤팩터+굴삭기 0.7㎥, A=77.7㎡/hr로 동일. 현행은 명칭을 비탈면 다짐으로 변경
8 판본 미확인(STmate 2024~2025 출력) 판본·인용 오류 미확인 9-3 임도성토면다짐 성토사면다짐 굴삭기0.7㎥ 산림청고시 2025-82호(2026-01-01) 3-6 임도성토면 다짐 4.24 임도성토면 다짐 8-18 임도성토면 다짐 9-17-1 FP-09-17-01 비탈면 다짐 미확인 추정 확정 같은 STmate 자료 안에서도 과거 절 인용이 3-6과 9-3으로 갈림. 과거 판본부터 확인 필요 공종·장비는 2017 3-6→2018 4.24→2024 8-18→현행 9-17-1로 이어짐. 현행 원문이 임도성토면 다짐, 굴착기 부착 유압식 진동 콤펙터+굴착기 0.7㎥, A=77.7㎡/hr를 직접 명시함. STmate의 9-3과 Q=716.8은 원문과 불일치하므로 절 대응만 확정하고 해당 번호·값 복사는 금지
9 판본 미확인(STmate 2024~2025 출력) 2018 통합 산림사업 표준품셈 4-16-4 파종공 씨뿌리기(성토면) 줄파종 산림청고시 2025-82호(2026-01-01) 선행 사방품셈 4-2-4 4.16.4 파종공 4-18 씨뿌리기(줄) 5-18 FP-05-18 씨뿌리기(줄) F0125 추정 확정 줄파종·씨뿌리기 명칭과 적용맥락 일치. 과거 원문·수량 미대조 2024·현행의 종자 2g, 비료 29.3g, 특별인부 0.00055, 보통인부 0.00838인/m가 STmate 구성과 일치
10 판본 미확인(STmate 2024~2025 출력) 2017 임도표준품셈 3-3-2 굴삭기 적용 암절취 암절취 굴삭기0.7㎥+대형브레카 산림청고시 2025-82호(2026-01-01) 3-3-2 암절취 4.3.3 암절취 8-3 암절취 9-4 / 9-4-1 / 9-4-2 FP-09-04 / FP-09-04-01 / FP-09-04-02 암절취 / 암파쇄 / 집토 F0241 / F0242 추정 확정 현행은 암절취를 암파쇄+집토 단계합산형으로 분리. 과거 한 절과 일대다 후보 2018 목차에 3-3-2→4.3.3이 직접 병기되고 2024·현행은 암파쇄+집토로 분리. 브레이커 작업능력 5.0·3.4·2.6㎥/hr와 굴삭기 0.7㎥ 계수가 동일한 일대다 대응
11 판본 미확인(STmate 2024~2025 출력) 2017 임도표준품셈 2-3 제근(100㎡당) 제근 산림청고시 2025-82호(2026-01-01) 2-3 제근 4.20.2 제근 8-22 제근 9-21 FP-09-21 제근 F0294 확정 밀림 보통인부 0.05인이 현행 9-21과 직접 일치 2018 목차에 2-3→4.20.2가 직접 병기되고 2024 8-22를 거쳐 현행 9-21로 이동. 밀림 보통인부 0.05인/100㎡ 일치
12 판본 미확인(STmate 2024~2025 출력) 2017 임도표준품셈 5-1-1 나 암절취 측구터파기(암) 기계100%(굴삭기 0.4㎥) 산림청고시 2025-82호(2026-01-01) 5-1-1 나 암절취 4.27.1 나 암절취 8-11 나 암절취 9-12-2 FP-09-12-02 암절취 F0259 추정 확정 측구터파기·암 조건으로 현행 절 후보 특정. 수량·장비조건 미대조 2018 목차에 5-1-1→4.27.1이 직접 병기됨. 2024·현행의 연암 Q=3.5, 치즐 0.006 및 측구터파기 조건이 동일. STmate 장비 규격 0.4㎥ 표기는 원문 0.7㎥와 불일치하므로 값 복사는 금지
13 판본 미확인(STmate 2024~2025 출력) 2017 임도표준품셈 3-7 가 층따기 층따기 산림청고시 2025-82호(2026-01-01) 3-7 층따기 4.25 층따기 8-19 층따기 9-18 FP-09-18 층따기 F0287 추정 확정 명칭 직접 일치하지만 과거 원문판이 없어 절 동일성 확정 불가 2018 목차에 3-7→4.25가 직접 병기되고 2017·2024·현행 모두 굴삭기 0.7㎥, K=0.9, f=1/1.3, E=0.7, Cm=22(180°)가 동일
14 판본 미확인(STmate 2024~2025 출력) 2017 임도표준품셈 3-3-1 토사 굴삭기 적용 토사절취(흙깎기) 굴삭기0.7㎥ 산림청고시 2025-82호(2026-01-01) 3-3-1 토사 4.3.2 토사깎기 8-2 토사깎기 9-3-2 FP-09-03-02 기계 F0240 추정 확정 현행 9-3 토사깍기의 기계 하위 절 후보. 수량·토질조건 미대조 2018 목차에 3-3-1→4.3.2가 직접 병기되고 2017·2024·현행의 굴삭기 0.7㎥, K=0.9, f=1/1.30, E=0.55~0.45, Cm=20(135°)가 동일
15 판본 미확인(STmate 2024~2025 출력) 2018 통합 산림사업 표준품셈 4-39 쇄석·혼합석 부설 혼합석부설 T=0.1m 산림청고시 2025-82호(2026-01-01) 7-2 쇄석·혼합석 부설(선행) 4.39 쇄석·혼합석 부설 10-4 쇄석·혼합석 부설 11-4 FP-11-04 쇄석․혼합석 부설 F0328 추정 확정 명칭 직접 일치하지만 두께·장비·수량 조건 미대조 2018 목차에 7-2→4.39가 직접 병기됨. 2024·현행의 굴삭기 0.7㎥, q=0.7, k=0.55, f=1, E=0.35, Cm=20(135°)가 동일. T=0.1m 환산은 별도 계산
@@ -144,3 +144,60 @@ def test_단끊기는_공정_줄을_더하고_작업반장을_20인당_1인_더
other = AxisResult()
match_table({"work_item_code": "FP-13-12-01"}, _단끊기, catalog, other)
assert not other.rows
_STONE = [*MACHINES, CatalogEntry("1033", "석공", "labor"), CatalogEntry("1019", "포장공", "labor")]
def test_인력_찰쌓기_두_단_머리는_hwpx_병합대로_돌과_쌓기를_가른다():
"""앞 세 돌은 골·켜 두 잎씩, 호박돌 및 야면석은 한 잎(행 병합) — 7 잎 × 석공·보통인부."""
table = {
"pum_table_id": "F-찰쌓기",
"pum_form": "requirement",
"basis_quantity": 1.0,
"basis_unit": "",
"condition_note": ["뒷길이 (㎝)", "견 치 돌", "깬 돌", "깬 잡 석", "호박돌 및 야면석"],
"raw_row": [
["골쌓기", "켜쌓기", "골쌓기", "켜쌓기", "골쌓기", "켜쌓기", "", ""],
["석 공 (인)", "보통인부 (인)"] * 7 + [""],
["25", *["-"] * 8, "0.12", "0.12", "0.10", "0.10", "0.08", "0.10"],
["35", "0.40", "0.40", "0.44", "0.44", "0.24", "0.24", "0.22", "0.22"]
+ ["0.20", "0.20", "0.18", "0.18", "0.11", "0.14"],
],
}
result = AxisResult()
match_table({"work_item_code": "FP-13-04-04"}, table, ResourceCatalog(entries=_STONE), result)
assert _codes(result, "견치돌 · 켜쌓기 · 뒷길이 35㎝") == {
"1033": Decimal("0.44"),
"1002": Decimal("0.44"),
}
assert _codes(result, "호박돌 및 야면석 · 뒷길이 35㎝") == {
"1033": Decimal("0.11"),
"1002": Decimal("0.14"),
}
assert not _codes(result, "견치돌 · 골쌓기 · 뒷길이 25㎝") # 「-」 — 그 뒷길이엔 품이 없음
assert "FP-13-04-04" not in result.partial_items
def test_콘크리트_포장_작업조는_두께마다_시공량으로_나눈다():
"""12-6 — 두께 칸이 작업조 칸과 엇갈려 병합(hwpx) · 후진 진입 칸은 「50%까지 감」 범위."""
table = {
"pum_table_id": "F-포장",
"pum_form": "reference",
"condition_note": ["배치인원(인)", "포장두께", "시공량(㎥)"],
"raw_row": [
[
"콘크리트믹서트럭 직접타설인경우",
"콘크리트믹서트럭 후진 진입 또는 경운기 등으로 운반인 경우",
],
["포장공", "3", "20㎝", "100", "좌측 시공량의 50%까지 감하여 적용한다."],
["30㎝", "150", "", "", ""],
["보통인부", "3", "", "", ""],
["40㎝", "200", "", "", ""],
],
}
result = AxisResult()
match_table({"work_item_code": "FP-12-06"}, table, ResourceCatalog(entries=_STONE), result)
assert _codes(result, "20㎝") == {"1019": Decimal("0.03"), "1002": Decimal("0.03")}
assert _codes(result, "40㎝")["1019"] == Decimal("0.015")
assert {r.amount_unit for r in result.rows} == {""} # 시공량 열 머리의 단위