From 389d0140186506a9ff178f59b47e259e72ce0d23 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Wed, 16 Sep 2026 15:48:20 +0900 Subject: [PATCH 1/2] =?UTF-8?q?fix(z01):=20=EC=BD=94=EB=8D=B1=EC=8A=A4=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EB=91=98=20=E2=80=94=20=EC=B2=A0=EA=B7=BC?= =?UTF-8?q?=20=EB=8B=A8=EA=B0=80=20=EC=B0=B8=EA=B3=A0=EA=B0=92=EC=9D=80=20?= =?UTF-8?q?B09=20=EA=B3=84=EC=82=B0=EA=B0=92=EC=9D=B4=EB=9D=BC=20=EA=B3=A0?= =?UTF-8?q?=EC=B9=A0=20=EC=B9=B8=EC=97=90=EC=84=9C=20=EB=B9=BC=EA=B3=A0=20?= =?UTF-8?q?=E3=80=8C=EA=B3=84=EC=82=B0=EA=B0=92=20=C2=B7=20=ED=91=9C?= =?UTF-8?q?=EC=8B=9C=20=EC=A0=84=EC=9A=A9=E3=80=8D=20=EA=B9=8C=EB=8B=AD?= =?UTF-8?q?=C2=B7=EC=8B=9D=EC=9D=84=20=EC=8B=A4=EC=9D=8C(=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=20=EC=9E=A3=EB=8C=80=20=E3=80=8C=EB=A7=88?= =?UTF-8?q?=EC=8A=A4=ED=84=B0=20=3D=20=EA=B3=84=EC=82=B0=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=20=EB=82=98=EC=98=A4=EB=8A=94=20=EA=B0=92=EC=9D=B4=20=EC=95=84?= =?UTF-8?q?=EB=8B=98=E3=80=8D)=20=C2=B7=20=EB=8F=8C=EC=8C=93=EA=B8=B0=20?= =?UTF-8?q?=EA=B0=88=EB=9E=98=20=ED=91=9C=EC=97=90=20=EA=B0=99=EC=9D=80=20?= =?UTF-8?q?1:0.3=20=EC=9D=B4=20=EB=91=90=20=EC=9E=90=EB=A6=AC=EC=9D=B8=20?= =?UTF-8?q?=EA=B9=8C=EB=8B=AD=20=EC=95=8C=EB=A6=BC(=ED=92=88=EC=85=88=2013?= =?UTF-8?q?-4-4=20[=EC=A3=BC]=E2=91=AA=20=EC=A7=81=EA=B3=A0=C2=B7=EC=84=B1?= =?UTF-8?q?=EC=A0=88=ED=86=A0=20=EC=B6=95=20=E2=86=94=20=EA=B5=90=EB=B3=B8?= =?UTF-8?q?=207-3=20=ED=98=95=EC=8B=9D=EB=B3=84=20=EA=B8=B0=EB=B3=B8?= =?UTF-8?q?=EA=B0=92=20=E2=80=94=20=EC=B6=95=EA=B3=BC=20=EA=B7=BC=EA=B1=B0?= =?UTF-8?q?=EA=B0=80=20=EB=8B=A4=EB=A6=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn --- Z01_MasterData/Z01_MasterData_BasePrices.py | 25 ++++++++++++++++--- .../Z01_MasterData_BasePrices_Tables.py | 10 +++++++- resources/tester/test_z01_base_tables.py | 25 ++++++++++++++++--- 3 files changed, 52 insertions(+), 8 deletions(-) diff --git a/Z01_MasterData/Z01_MasterData_BasePrices.py b/Z01_MasterData/Z01_MasterData_BasePrices.py index 7e4e4e91..6de5c298 100644 --- a/Z01_MasterData/Z01_MasterData_BasePrices.py +++ b/Z01_MasterData/Z01_MasterData_BasePrices.py @@ -344,7 +344,18 @@ def _sorted(rows: list[dict[str, Any]], column: str, desc: bool) -> list[dict[st #: 돌쌓기 표준경사는 B06 횡단설계가 같은 값으로 기울기를 그림 — 아홉 중 **그림에도 닿는** 하나(브레인). _SLOPE_NOTICE = "고치면 횡단 도면 모양도 함께 바뀜 — B06 횡단설계가 같은 값으로 벽 기울기를 그림" -_EXTRA_NOTICE = {"rate": _RATE_NOTICE, "masonry_slope": _SLOPE_NOTICE} +#: 같은 「1:0.3」 이 두 자리에 있음(코덱스 검증 2) — 값은 같으나 **축과 근거가 다름**. +#: masonry_slope = 품셈 13-4-4 [주]⑪ 표준경사(직고 · 메/찰 · 성토/절토 축 · 원문표) +#: masonry_class.face_slope = 교본 7-3 돌흙막이(구조물 **형식**별 기본값 · 판정이 안 되는 자리의 종전값) +_CLASS_NOTICE = ( + "전면 기울기 기본값은 **교본 7-3 형식별 값** — 품셈 표준경사(직고·성토/절토 축)는 「돌쌓기 표준경사」 표에 따로 있음" + " · 같은 1:0.3 이라도 **축과 근거가 다름**(판정이 안 되는 자리에서만 이 값으로 섬)" +) +_EXTRA_NOTICE = { + "rate": _RATE_NOTICE, + "masonry_slope": _SLOPE_NOTICE, + "masonry_class": _CLASS_NOTICE, +} def notice(kind: str) -> list[str]: @@ -357,12 +368,18 @@ def spec(kind: str, columns: list[str]) -> dict[str, Any]: if kind == "machine": return machine.spec(columns) if kind in base_tables.SPEC: - editable = [c for c in base_tables.SPEC[kind]["editable"] if c in columns] + spec_of = base_tables.SPEC[kind] + editable = [c for c in spec_of["editable"] if c in columns] axis = {a for r in base_rows(kind) for a in r.get("@axis", ())} + told = spec_of.get("locked") or {} # 계산값처럼 까닭이 따로인 칸 return { "editable": editable, - "locked": {c: _KEY if c in axis else _SOURCE for c in columns if c not in editable}, - "formula": {}, + "locked": { + c: told.get(c) or (_KEY if c in axis else _SOURCE) + for c in columns + if c not in editable + }, + "formula": {k: v for k, v in (spec_of.get("formula") or {}).items() if k in columns}, } editable = { "labor": ["daily_wage_krw"], diff --git a/Z01_MasterData/Z01_MasterData_BasePrices_Tables.py b/Z01_MasterData/Z01_MasterData_BasePrices_Tables.py index dca7b6e3..05640fa0 100644 --- a/Z01_MasterData/Z01_MasterData_BasePrices_Tables.py +++ b/Z01_MasterData/Z01_MasterData_BasePrices_Tables.py @@ -362,9 +362,17 @@ SPEC: dict[str, dict[str, Any]] = { "editable": ["reuse_count", "ratio_pct", "daily_area_m2"], }, "rebar_complexity": { + # ⚠ 단가 참고값은 **B09 가 자재 단가로 셈한 값**이라 고칠 칸이 아님(코덱스 검증 1 · 2026-09-16). + # 사용자 잣대 「마스터 = 계산으로 나오는 값이 아니라 값 그 자체」와 어긋나 editable 에서 뺌. "file": "rebar_complexity", "build": _rebar_complexity, - "editable": ["price_krw_per_ton"], + "editable": [], + "locked": { + "price_krw_per_ton": "계산값 — B09 가 자재 단가로 셈(표시 전용 · 밑값을 고쳐야 바뀜)" + }, + "formula": { + "price_krw_per_ton": "갈래별 이형철근 단가 참고값 = B09 가 자재 단가에서 셈 — 표시 전용(B08 계산에 안 듦)" + }, }, "timber_structure_class": { "file": "timber_structure_class", diff --git a/resources/tester/test_z01_base_tables.py b/resources/tester/test_z01_base_tables.py index 2ff80938..19fa7877 100644 --- a/resources/tester/test_z01_base_tables.py +++ b/resources/tester/test_z01_base_tables.py @@ -66,9 +66,8 @@ def test_아홉이_기초단가와_같은_길로_섬(client: TestClient) -> None assert table["total"] == len(table["rows"]) > 0, kind ids = _ids(table) assert len(ids) == len(set(ids)), kind # ⭐ 열쇠가 겹치면 덮개가 엉뚱한 줄에 붙음 - assert table["editable"] and set(table["editable"]) <= { - c["key"] for c in table["columns"] - }, kind + assert set(table["editable"]) <= {c["key"] for c in table["columns"]}, kind + assert table["editable"] or kind == "rebar_complexity", kind # 철근은 값이 다 계산·글 assert set(table["locked"]) | set(table["editable"]) == set(table["sortable"]), kind axis = {a for r in base_prices.base_rows(kind) for a in r.get("@axis", ())} assert {table["locked"][c] for c in axis if c in table["locked"]} == { @@ -252,3 +251,23 @@ def test_꺼낸_값은_코드가_쓰던_것과_원문과_같음() -> None: for ton, speeds in _DOZER_SPEEDS[track].items(): for gear, (forward, reverse) in speeds.items(): assert (str(forward), str(reverse)) == original[ton][gear], (track, ton, gear) + + +def test_계산값은_고칠_칸이_아니고_까닭이_보임(client: TestClient) -> None: + """코덱스 검증 1 — 철근 단가 참고값은 **B09 계산값**인데 고칠 수 있는 마스터 값으로 앉아 있었음.""" + rebar = _get(client, "rebar_complexity", size=10) + assert "price_krw_per_ton" not in rebar["editable"] + assert "계산값" in rebar["locked"]["price_krw_per_ton"] + assert "표시 전용" in rebar["formula"]["price_krw_per_ton"] + machine = _get(client, "machine", size=1) # 기계 계산 넷도 같은 꼴(잠김 + 식) + for column in ("hourly_loss_krw", "hourly_fuel_krw", "hourly_operator_krw", "hourly_total_krw"): + assert "계산값" in machine["locked"][column] and column in machine["formula"] + + +def test_같은_기울기가_두_자리인_까닭을_표가_말함(client: TestClient) -> None: + """코덱스 검증 2 — 값은 같은 1:0.3 이나 **축과 근거가 다름**(품셈 직고·성절토 ↔ 교본 형식별).""" + notice = _get(client, "masonry_class", size=1)["notice"] + assert any("교본 7-3" in line and "돌쌓기 표준경사" in line for line in notice) + assert any("축과 근거가 다름" in line for line in notice) + slope = _get(client, "masonry_slope", size=1)["notice"] + assert any("횡단 도면" in line for line in slope) # 표준경사 쪽 알림은 그대로 From 8a563f51482792a23dc3226998d59ae0fc202472 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Wed, 16 Sep 2026 15:51:24 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat(z01):=20=ED=92=88=EC=85=88=20=EA=B8=B0?= =?UTF-8?q?=EC=A4=80=EC=97=90=20=E3=80=8C=EA=B8=B0=EA=B3=84=20=EC=9E=91?= =?UTF-8?q?=EC=97=85=EB=9F=89=E3=80=8D(machine=5Fproductivity)=20=EB=8B=A8?= =?UTF-8?q?=EC=B6=94=20=EB=8D=94=ED=95=A8=20=E2=80=94=20=EC=95=84=ED=99=89?= =?UTF-8?q?=20=E2=86=92=20=EC=97=B4(=EB=B6=88=EB=8F=84=EC=A0=80=20?= =?UTF-8?q?=EC=86=8D=EB=8F=84=C2=B7=EC=82=BD=EB=82=A0=C2=B7=EB=8D=A4?= =?UTF-8?q?=ED=94=84=20=EC=9A=B4=EB=B0=98=20=EB=B0=91=EA=B0=92=2047?= =?UTF-8?q?=EC=A4=84)=20=C2=B7=20=EC=9D=B4=EB=A6=84=ED=91=9C=20=EC=98=A4?= =?UTF-8?q?=EA=B8=B0=20=EC=A0=84=EC=9D=B4=EB=9D=BC=20=EC=97=B4=2015=20?= =?UTF-8?q?=EB=8A=94=20=EC=98=81=EB=AC=B8=EC=9C=BC=EB=A1=9C=20=EB=9C=B8(?= =?UTF-8?q?=EC=A0=95=EC=83=81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq --- Z01_MasterData/Z01_MasterData_Api_Fetch.ts | 1 + ui_template/ui_template_locale_b3.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/Z01_MasterData/Z01_MasterData_Api_Fetch.ts b/Z01_MasterData/Z01_MasterData_Api_Fetch.ts index 7a76b9e7..ddfd40ae 100644 --- a/Z01_MasterData/Z01_MasterData_Api_Fetch.ts +++ b/Z01_MasterData/Z01_MasterData_Api_Fetch.ts @@ -63,6 +63,7 @@ export const SPEC_KINDS = [ "masonry_slope", "masonry_back_length", "stone_kind", + "machine_productivity", "masonry_class", ] as const; diff --git a/ui_template/ui_template_locale_b3.ts b/ui_template/ui_template_locale_b3.ts index 866bf245..810d777a 100644 --- a/ui_template/ui_template_locale_b3.ts +++ b/ui_template/ui_template_locale_b3.ts @@ -157,6 +157,7 @@ export const ui_locales_b3 = { Z01_MasterData_Base_masonry_slope: ["돌쌓기 경사", "Masonry slope"], Z01_MasterData_Base_masonry_back_length: ["뒷길이", "Back length"], Z01_MasterData_Base_stone_kind: ["돌종류", "Stone kind"], + Z01_MasterData_Base_machine_productivity: ["기계 작업량", "Machine productivity"], Z01_MasterData_Base_masonry_class: ["갈래", "Masonry class"], Z01_MasterData_Overrides: ["고친 것 모아 보기", "Edited values"], Z01_MasterData_Edit_Can: [