From 26d9092a9bd6b0f39ed2b2eb573ae6f4499f76b1 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 14 Sep 2026 18:01:34 +0900 Subject: [PATCH] =?UTF-8?q?fix(b09):=20=EC=9B=90=EA=B0=80=EA=B3=84?= =?UTF-8?q?=EC=82=B0=20=EB=82=98=EB=A8=B8=EC=A7=80=20=EC=84=B8=20=ED=98=95?= =?UTF-8?q?=EC=8B=9D=20=EC=82=AC=EC=9C=A0=EB=A5=BC=20=EC=9A=94=EC=9C=A8?= =?UTF-8?q?=ED=91=9C=20=EC=85=8B=20=EA=B2=B0=EB=A1=A0=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=E2=80=94=20=EC=8B=A4=EC=A0=81=ED=98=95=20=E3=80=8C=EC=9E=84?= =?UTF-8?q?=EB=8F=84=20=EB=AF=B8=EC=A0=81=EC=9A=A9=20=E2=80=94=20100?= =?UTF-8?q?=EC=96=B5=20=EB=AF=B8=EB=A7=8C(=EC=98=88=EC=A0=95=EA=B0=80?= =?UTF-8?q?=EA=B2=A9=EC=9E=91=EC=84=B1=EA=B8=B0=EC=A4=80=20=C2=A737?= =?UTF-8?q?=E2=91=A1)=E3=80=8D=20=C2=B7=20=EC=88=98=EA=B3=B5=20=EC=84=B8?= =?UTF-8?q?=EB=B6=80=20=EA=B2=BD=EB=B9=84=20=E3=80=8C=EB=B2=95=EC=A0=95=20?= =?UTF-8?q?=EC=9A=94=EC=9C=A8=20=EC=97=86=EC=9D=8C=20=E2=80=94=20=EA=B0=92?= =?UTF-8?q?=EC=9D=84=20=EB=84=A3=EC=96=B4=20=EC=A3=BC=EC=8B=AD=EC=8B=9C?= =?UTF-8?q?=EC=98=A4(=C2=A734=E2=91=A0)=E3=80=8D=20=C2=B7=20=EC=8B=A4?= =?UTF-8?q?=EC=A0=81=20=EC=95=88=EC=A0=84=ED=91=9C=EB=8A=94=20=EA=B3=A0?= =?UTF-8?q?=EC=8B=9C=20=ED=8E=B8=EC=9E=85=EC=9D=B4=EB=82=98=20=EC=8B=A4?= =?UTF-8?q?=EC=A0=81=ED=98=95=20=EB=AF=B8=EC=A0=81=EC=9A=A9=20=C2=B7=20?= =?UTF-8?q?=ED=83=AD=20=EB=A7=90=ED=92=8D=EC=84=A0=EB=8F=84=20=EA=B0=99?= =?UTF-8?q?=EC=9D=80=20=EB=A7=90?= 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 --- .../B09_Estimation_CostSheet_Forms.py | 21 ++++++++++++++----- .../B09_Estimation_Router_CostSheet.py | 4 ++-- .../B09_Estimation_UI_Tab_CostSheet.ts | 10 +++++++-- resources/tester/test_b09_cost_sheet_forms.py | 10 +++++++++ 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/B09_Estimation/B09_Estimation_CostSheet_Forms.py b/B09_Estimation/B09_Estimation_CostSheet_Forms.py index e03d5ea5..4d266d48 100644 --- a/B09_Estimation/B09_Estimation_CostSheet_Forms.py +++ b/B09_Estimation/B09_Estimation_CostSheet_Forms.py @@ -3,8 +3,10 @@ 근거: STmate 분석 `12_원가계산_4형식과_RATE표_복원.md` §4~6 (DFM 복원 서식) · `35_형식과_단계의_공통과_차이.md` §1·§2 (공통 사슬 · 형식별 차이 · 12번 정정 반영). -⚠ **금액을 안 낸다** — 요율 데이터가 없다(2026-09-14 확인: 수공 세부 경비 율표 `RateB` · - 표준시장단가 제비율 `RATE_RA`·`RATE_RA1`·실적 안전표 `An_A`). 줄마다 무엇이 막혔는지만 적는다. +⚠ **금액을 안 낸다** — 2026-09-14 요율표 셋 결론(PLAN 7장): 실적형 제비율 `RATE_RA`·`RATE_RA1` 은 + **임도 미적용**(100억 미만 · 예정가격작성기준 §37②) · 수공 세부 경비 `RateB` 는 **법정 요율 없음** + (업체 실측 대상 §34① — 설계자 입력 칸) · 실적 안전표 `An_A` 는 고시 편입(제4조·별표1)이나 실적형 + 자체가 임도 미적용. 줄마다 무엇이 막혔는지만 적는다. **0 원으로 채우지 않는다** — 0 원은 「없다」가 아니라 「공짜」로 읽혀 총액이 조용히 작아진다. ⚠ 35번 §5 미확인은 「확인 대기」로 세운다 — 지금 남은 것은 **실적형 이윤 밑수** 하나다. 실적수공 안전관리비는 2026-09-14 코덱스가 STmate 원문에서 **있음**을 확인해 별도내역으로 내렸다. @@ -17,9 +19,18 @@ from typing import Any #: 줄 상태 — 화면이 그대로 보인다. COMMON = "공통 요율 — 일반형식과 같은 표(형식 전체가 서면 금액이 섬)" SEPARATE = "별도내역 — 요율이 아니라 따로 산출한 금액" -RATE_B = "요율표 미확보 — 수공 세부 경비 율표(STmate RateB)" -RATE_RA = "요율표 미확보 — 표준시장단가(실적공사비) 제비율(STmate RATE_RA·RATE_RA1)" -RATE_AN_A = "요율표 미확보 — 실적 산업안전보건관리비 표(STmate An_A·An_A1)" +RATE_B = ( + "법정 요율 없음 — 값을 넣어 주십시오(업체 실측 대상 · 예정가격작성기준 §34①) · 수공 세부 경비" + "(STmate RateB) · 입력 칸은 수공형식 금액 셈과 함께 섬" +) +RATE_RA = ( + "임도 미적용 — 100억 미만(예정가격작성기준 §37②) · 표준시장단가(실적공사비) 제비율" + "(STmate RATE_RA·RATE_RA1)" +) +RATE_AN_A = ( + "임도 미적용 — 실적형 형식 전체가 100억 미만 임도에 안 씀(§37②) · 요율 자체는 고시 편입" + "(건설업 산업안전보건관리비 계상 및 사용기준 제4조·별표1)" +) TOTAL = "합계 — 위 줄이 다 서야 섬" PENDING_PROFIT = "확인 대기 — 실적형 이윤 밑수가 복원식과 화면 문구가 충돌(35번 §5-1)" DIRECT = "설계내역서 합계에서 옴" diff --git a/B09_Estimation/B09_Estimation_Router_CostSheet.py b/B09_Estimation/B09_Estimation_Router_CostSheet.py index ac85aa7c..19fd0f35 100644 --- a/B09_Estimation/B09_Estimation_Router_CostSheet.py +++ b/B09_Estimation/B09_Estimation_Router_CostSheet.py @@ -87,8 +87,8 @@ def cost_from_bill(root: str, bill: dict[str, Any], direct: dict[str, Decimal]): async def get_cost_sheet(project_id: UUID, form: str = "general") -> JSONResponse: """원가계산서 한 장 — 서식 줄 · 기준 입력 선택지 · 상태줄. - `form` — 일반형식만 금액을 냄. 수공·실적일반·실적수공은 **서식 차례만**(요율표 미확보 줄은 - 막고 사유 · 0 원으로 안 채움). + `form` — 일반형식만 금액을 냄. 수공·실적일반·실적수공은 **서식 차례만**(실적형 임도 미적용 · + 수공 세부 경비 법정 요율 없음 줄은 막고 사유 · 0 원으로 안 채움). """ from B09_Estimation.B09_Estimation_Router import get_bill from common_util.common_util_project_settings import estimation_settings diff --git a/B09_Estimation/B09_Estimation_UI_Tab_CostSheet.ts b/B09_Estimation/B09_Estimation_UI_Tab_CostSheet.ts index 03675497..dd94423e 100644 --- a/B09_Estimation/B09_Estimation_UI_Tab_CostSheet.ts +++ b/B09_Estimation/B09_Estimation_UI_Tab_CostSheet.ts @@ -73,6 +73,12 @@ const FORMS = [ ]; /** DFM 본문 아래 탭 이름 그대로. */ const FORM_TABS = ["general", "sugong", "actual_general", "actual_sugong"]; +/** 탭 말풍선 — 금액이 안 서는 까닭(2026-09-14 요율표 셋 결론 · 줄 사유는 서버 문구). */ +const FORM_TAB_NOTES: Record = { + sugong: "서식 차례만 — 세부 경비 법정 요율 없음(값 입력 대기 · 예정가격작성기준 §34①)", + actual_general: "서식 차례만 — 임도 미적용(100억 미만 · 예정가격작성기준 §37②)", + actual_sugong: "서식 차례만 — 임도 미적용(100억 미만 · 예정가격작성기준 §37②)", +}; /** 프로젝트별 고른 형식 탭 — 일반형식만 금액이 섬. */ const formOf = new Map(); @@ -320,7 +326,7 @@ function drawBody(ctx: B09TabContext, sheet: CostSheetDto, reload: () => void): label.append(el("span", "", row.label)); if (row.formula) label.append(el("span", "b09cs__formula", `<${row.formula}>`)); label.title = [row.formula, row.note].filter(Boolean).join(" · "); - // 금액 없는 줄 — 「-」 + 막힌 까닭(요율표 미확보 · 확인 대기). 0 원으로 안 보임. + // 금액 없는 줄 — 「-」 + 막힌 까닭(임도 미적용 · 법정 요율 없음 · 확인 대기). 0 원으로 안 보임. if (row.amount_krw === null && row.blocked) label.append(el("span", "b09cs__warn", row.note)); line.append( label, @@ -334,7 +340,7 @@ function drawBody(ctx: B09TabContext, sheet: CostSheetDto, reload: () => void): for (const key of FORM_TABS) { const tab = el("button", "b09cs__tab", sheet.form_labels[key] ?? key); tab.setAttribute("aria-selected", String(key === sheet.form_key)); - if (key !== "general") tab.title = "서식 차례만 — 요율표 미확보로 금액 안 섬"; + if (FORM_TAB_NOTES[key]) tab.title = FORM_TAB_NOTES[key]; tab.addEventListener("click", () => { formOf.set(ctx.projectId as string, key); reload(); diff --git a/resources/tester/test_b09_cost_sheet_forms.py b/resources/tester/test_b09_cost_sheet_forms.py index adc2035f..cd89fc7f 100644 --- a/resources/tester/test_b09_cost_sheet_forms.py +++ b/resources/tester/test_b09_cost_sheet_forms.py @@ -60,3 +60,13 @@ def test_실적일반_보증_밑수는_직접공사비_환경은_재료직노산 def test_모르는_형식은_멈춘다() -> None: with pytest.raises(ValueError): form_rows("general") + + +@pytest.mark.parametrize("form", ["sugong", "actual_general", "actual_sugong"]) +def test_사유는_요율표_미확보가_아니라_실제_결론(form) -> None: + """2026-09-14 요율표 셋 결론(PLAN 7장) — 「자료를 못 구함」 이 아니라 실적형은 **임도 미적용** + (예정가격작성기준 §37② 100억 미만) · 수공 세부 경비는 **법정 요율 없음**(§34① 업체 실측).""" + notes = [row["note"] for row in form_rows(form)] + assert not any("요율표 미확보" in note for note in notes), notes + assert "임도 미적용" in RATE_RA and "§37②" in RATE_RA + assert "법정 요율 없음" in RATE_B and "값을 넣어 주십시오" in RATE_B and "§34①" in RATE_B