diff --git a/B09_Estimation/B09_Estimation_ResourceAxis.py b/B09_Estimation/B09_Estimation_ResourceAxis.py index 1c9e4525..84b72ba5 100644 --- a/B09_Estimation/B09_Estimation_ResourceAxis.py +++ b/B09_Estimation/B09_Estimation_ResourceAxis.py @@ -20,6 +20,7 @@ from __future__ import annotations +import hashlib import json import os import re @@ -526,6 +527,23 @@ def build_resource_axis(master: dict[str, Any], catalog: ResourceCatalog) -> Axi OUTPUT_SUBPATH = ("resources", "data_cost_resource_axis") +def _master_file_fingerprint(master: dict[str, Any]) -> dict[str, str]: + """공종 마스터 **파일 자체**의 지문. 낡은 파생물을 드러내는 유일한 근거다. + + `dataset_version`(품셈 원판 지문)은 마스터가 다시 생성돼도 그대로라, 그것만 + 적어 두면 「내 자원 축이 옛 마스터에서 나왔다」는 사실이 안 보인다. + """ + effective_date = master.get("effective_date", "") + file_name = f"work_item_master_{effective_date}.json" + path = os.path.join(_project_root(), *_MASTER_SUBPATH, file_name) + try: + with open(path, "rb") as handle: + digest = hashlib.sha256(handle.read()).hexdigest() + except OSError: + return {"file": file_name, "sha256": ""} + return {"file": file_name, "sha256": digest} + + def write_resource_axis( result: AxisResult, master: dict[str, Any], @@ -546,6 +564,9 @@ def write_resource_axis( "effective_date": effective_date, # 어느 공종 축 판에 붙인 것인지 — 세 쪽을 그대로 옮겨 적는다(PLAN 9-2). "source_dataset_version": master.get("dataset_version", {}), + # ⚠ 위 지문은 **품셈 원판**의 것이라 B08 이 마스터를 다시 생성해도 안 움직인다. + # 낡음을 실제로 드러내려면 **마스터 파일 자체의 지문**이 있어야 한다. + "source_master_file": _master_file_fingerprint(master), "policy": { "axis": "resource_only", "work_item_axis_owner": "B08", diff --git a/resources/data_cost_resource_axis/resource_axis_2026-01-01.json b/resources/data_cost_resource_axis/resource_axis_2026-01-01.json index 5774d43a..7b3a2e23 100644 --- a/resources/data_cost_resource_axis/resource_axis_2026-01-01.json +++ b/resources/data_cost_resource_axis/resource_axis_2026-01-01.json @@ -1015,6 +1015,30 @@ "resource_spec": "", "work_item_code": "FP-12-18" }, + { + "amount": "0.07", + "amount_unit": "", + "pum_form": "requirement", + "pum_table_id": "F0367", + "raw_row_index": 3, + "resource_code": "1007", + "resource_kind": "labor", + "resource_name": "형틀목공", + "resource_spec": "", + "work_item_code": "FP-12-20" + }, + { + "amount": "0.05", + "amount_unit": "", + "pum_form": "requirement", + "pum_table_id": "F0367", + "raw_row_index": 4, + "resource_code": "1002", + "resource_kind": "labor", + "resource_name": "보통인부", + "resource_spec": "", + "work_item_code": "FP-12-20" + }, { "amount": "0.034", "amount_unit": "", @@ -1087,6 +1111,18 @@ "resource_spec": "", "work_item_code": "FP-12-24-01" }, + { + "amount": "0.12", + "amount_unit": "", + "pum_form": "requirement", + "pum_table_id": "F0378", + "raw_row_index": 2, + "resource_code": "1027", + "resource_kind": "labor", + "resource_name": "미장공", + "resource_spec": "", + "work_item_code": "FP-12-28" + }, { "amount": "0.004", "amount_unit": "", @@ -1407,13 +1443,17 @@ "file": "pum_forest_2026.json", "sha256": "111208fd482dcfc3b8c8dd58004b153382fc46555d0d00985c19b45ebbc2e0dd" }, + "source_master_file": { + "file": "work_item_master_2026-01-01.json", + "sha256": "593653135d5a2871180e7a3921f9238629b275438ef47230412aa21e9bdd80c0" + }, "stats": { - "rows": 116, + "rows": 119, "skipped_forms": { "coefficient": 19, - "reference": 94, - "undetermined": 83 + "reference": 98, + "undetermined": 76 }, - "unmatched": 348 + "unmatched": 355 } } \ No newline at end of file diff --git a/resources/data_cost_resource_axis/unmatched_2026-01-01.json b/resources/data_cost_resource_axis/unmatched_2026-01-01.json index 915d08fc..b2414650 100644 --- a/resources/data_cost_resource_axis/unmatched_2026-01-01.json +++ b/resources/data_cost_resource_axis/unmatched_2026-01-01.json @@ -1772,6 +1772,24 @@ "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", "work_item_code": "FP-12-18" }, + { + "cell": "강관 동바리", + "pum_table_id": "F0367", + "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", + "work_item_code": "FP-12-20" + }, + { + "cell": "외관(60.6mm×2.3mm)", + "pum_table_id": "F0367", + "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", + "work_item_code": "FP-12-20" + }, + { + "cell": "잡재료비(재료비의)", + "pum_table_id": "F0367", + "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", + "work_item_code": "FP-12-20" + }, { "cell": "아스팔트(㏊-500)", "pum_table_id": "F0368", @@ -1844,6 +1862,30 @@ "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", "work_item_code": "FP-12-27-02" }, + { + "cell": "실런트", + "pum_table_id": "F0377", + "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", + "work_item_code": "FP-12-27-03" + }, + { + "cell": "인력(설치비)", + "pum_table_id": "F0377", + "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", + "work_item_code": "FP-12-27-03" + }, + { + "cell": "에폭시 접착제", + "pum_table_id": "F0378", + "reason": "규격이 없어 같은 이름 여럿 중 고를 수 없음", + "work_item_code": "FP-12-28" + }, + { + "cell": "시너", + "pum_table_id": "F0378", + "reason": "규격이 없어 같은 이름 여럿 중 고를 수 없음", + "work_item_code": "FP-12-28" + }, { "cell": "결속선(R-0.9mm)", "pum_table_id": "F0387",