diff --git a/B09_Estimation/B09_Estimation_ResourceAxis.py b/B09_Estimation/B09_Estimation_ResourceAxis.py index b964d3d1..f001cf5f 100644 --- a/B09_Estimation/B09_Estimation_ResourceAxis.py +++ b/B09_Estimation/B09_Estimation_ResourceAxis.py @@ -574,8 +574,10 @@ def match_table( # 정상 자원이 조용히 사라진다(2026-09-07 실측 — 부분일치 필터가 매칭 14건을 # 지우고 있었음). 카탈로그에 있는 이름은 **정의상 자원**이다. # 범위 별칭 — 그 공종 범위 안에서만 카탈로그 쪽 이름으로 바꾼다(「화약공」 → 화약취급공). + # 별칭이 코드를 적었으면 그 항목으로 곧장(규격이 조인 키인 기계가 다시 흐려지지 않게). + entry = scoped_alias_entry(catalog, name_cell, node["work_item_code"]) name_cell = apply_scoped_alias(catalog, name_cell, node["work_item_code"]) - entry = _resolve_cell(catalog, name_cell, [name_cell, *value_cells]) + entry = entry or _resolve_cell(catalog, name_cell, [name_cell, *value_cells]) if entry is None: if is_non_resource_label(name_cell): continue # 머리글·소계 — 못 맞춘 목록에도 안 올린다 @@ -699,6 +701,7 @@ from B09_Estimation.B09_Estimation_ResourceAxis_Sources import ( # noqa: E402 from B09_Estimation.B09_Estimation_ResourceAxis_Join import ( # noqa: E402 apply_scoped_alias, pick_by_spec, + scoped_alias_entry, resolve_family, text_spec_candidates, unmatched_reason, diff --git a/B09_Estimation/B09_Estimation_ResourceAxis_Join.py b/B09_Estimation/B09_Estimation_ResourceAxis_Join.py index 778506f4..8716e524 100644 --- a/B09_Estimation/B09_Estimation_ResourceAxis_Join.py +++ b/B09_Estimation/B09_Estimation_ResourceAxis_Join.py @@ -107,6 +107,22 @@ def load_scoped_aliases() -> list[dict[str, str]]: return load_aliases("resource") +def scoped_alias_entry( + catalog: ResourceCatalog, name_cell: str, work_item_code: str +) -> CatalogEntry | None: + """범위 별칭이 가리키는 **항목 그 자체**(코드로 곧장) — 없으면 `None`. + + ⚠ 이름만 돌려주면 규격이 조인 키인 기계(소형브레이커 공압식 넷)는 다시 「규격 미정」 으로 + 흐려짐 — 별칭이 코드를 적었으면 그 코드가 곧 답(2026-09-14 · 9-19-3 소형브레이커). + """ + wanted = _normalize(name_cell) + for row in catalog.scoped_aliases: + if _normalize(row["from"]) != wanted or not in_scope(work_item_code, row["scope"]): + continue + return next((entry for entry in catalog.entries if entry.code == row["to"]), None) + return None + + def apply_scoped_alias(catalog: ResourceCatalog, name_cell: str, work_item_code: str) -> str: """범위 안이면 카탈로그 쪽 **이름**으로 바꾼다. 범위 밖이거나 대상 코드가 없으면 원문 그대로.""" wanted = _normalize(name_cell) diff --git a/resources/data_aliases/aliases_2026-01-01.json b/resources/data_aliases/aliases_2026-01-01.json index 22ba4693..d1bfac04 100644 --- a/resources/data_aliases/aliases_2026-01-01.json +++ b/resources/data_aliases/aliases_2026-01-01.json @@ -11,6 +11,14 @@ "pum_edition": "2026-01-01", "basis": "노임 표준 직종명은 「화약취급공」(노임 카탈로그 1016) — 품셈 9-5 표가 줄여 적음. 2026-09-13 사용자 위임 판정(브레인)" }, + { + "axis": "resource", + "from": "소형브레이커", + "to": "5210-0010", + "scope": "FP-09-19-03", + "pum_edition": "2026-01-01", + "basis": "9-19-3 비탈면 고르기(발파암) 표가 규격을 안 적음 — 그 값 「(2.45+3.05)/2/10㎡」 은 9-19-1 토사면 고르기 표(연암 2.45·보통암+경암 3.05)에서 왔고 9-19-1 [주]① 「소형브레이커는 1㎥/분」 → 소형브레이커(공압식) 1.0㎥/min(9-19-3 ← 9-19-1 표 ← [주]①, 값의 출처가 규격을 정함). 2026-09-14 브레인 승인" + }, { "axis": "variant", "from": "리핑암", diff --git a/resources/tester/test_b09_table_reading_2.py b/resources/tester/test_b09_table_reading_2.py index d6fa7a81..6fe7209c 100644 --- a/resources/tester/test_b09_table_reading_2.py +++ b/resources/tester/test_b09_table_reading_2.py @@ -40,3 +40,21 @@ def test_육상_발파암_1_3m_들어내기는_0_1m_로_읽고_고른_값_버린 assert not left, left source = build.factor_sources.get(code, "") assert "육상 발파암(0~1m)" in source and "자기 참조" in source, source + + +def test_비탈면_고르기_발파암_소형브레이커는_1_0_규격으로_코드에_이어짐() -> None: + """④ 9-19-3 값 「(2.45+3.05)/2/10㎡」 ← 9-19-1 표 ← 9-19-1 [주]① 「소형브레이커는 1㎥/분」 — + 값의 출처가 규격을 정함(2026-09-14 브레인 승인). 범위 별칭이 **코드로** 곧장 이음(이름만 돌려주면 + 규격 후보 넷이 다시 흐려짐). 「어어호스」 는 원문 오기 + 카탈로그에 호스 없음 → 못 붙은 줄 그대로.""" + from B09_Estimation.B09_Estimation_ResourceAxis import ( + build_resource_axis, + load_combined_catalog, + ) + + axis = build_resource_axis(load_work_item_master(), load_combined_catalog()) + rows = [ + (r.resource_code, str(r.amount)) for r in axis.rows if r.work_item_code == "FP-09-19-03" + ] + assert ("5210-0010", "0.275") in rows, rows + left = [u.cell for u in axis.unmatched if u.work_item_code == "FP-09-19-03"] + assert left == ["어어호스(3/4인치)"], left