From 71d643b4562c1bbfe1a8e5c484c8b0e690503b6a Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 14 Sep 2026 02:05:34 +0900 Subject: [PATCH] =?UTF-8?q?refactor(b09):=20=EC=98=9B=20=ED=83=AD=20?= =?UTF-8?q?=EC=98=AE=EA=B8=B0=EA=B8=B0=20=E2=91=A3=20=EC=A4=91=EA=B8=B0?= =?UTF-8?q?=EA=B2=BD=EB=B9=84=EA=B3=84=EC=82=B0=EC=84=9C=20=E2=80=94=20?= =?UTF-8?q?=EC=98=9B=20BaseData=20=EC=97=90=EC=84=9C=20UI=5FMachineExpense?= =?UTF-8?q?=20=EB=A1=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 화면 확인: 옮기기 전후 같음 — 중기 탭 표 19 · 줄 171 · 계산서 18 장(불도저 19 손료계수 1811 · 시간당 손료 35,884.965) Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq --- .../B09_Estimation_UI_MachineExpense.ts | 122 ++++++++++++++++++ .../B09_Estimation_UI_Tab_Machine.ts | 6 +- 2 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 B09_Estimation/B09_Estimation_UI_MachineExpense.ts diff --git a/B09_Estimation/B09_Estimation_UI_MachineExpense.ts b/B09_Estimation/B09_Estimation_UI_MachineExpense.ts new file mode 100644 index 00000000..cce33926 --- /dev/null +++ b/B09_Estimation/B09_Estimation_UI_MachineExpense.ts @@ -0,0 +1,122 @@ +/* ============================================================================= + * B09_Estimation_UI_MachineExpense.ts + * 각종 중기경비계산서 — 기종마다 한 장(별표2 (5)(가) 아홉째) · 중기 탭 아래에 붙음 + * + * - 옛 `B09_Estimation_UI_BaseData.ts` 의 중기경비계산서를 그대로 옮김(PLAN 12장 옛 탭 옮기기). + * - ⚠ 목록표가 「얼마」라면 이 장은 **왜 그 값인가** — 계산 과정을 감추지 않음. + * ========================================================================== */ + +import { API_BASE_URL } from "@config/config_frontend"; +import { el } from "./B09_Estimation_UI_Sheet"; +import { head, infoTable, money, note } from "./B09_Estimation_UI_Table"; + +export interface MachineExpenseDto { + summary: string; + notes: string[]; + sheets: Array<{ + machine_code: string; + name: string; + spec: string; + price_thousand_krw: string | null; + economic_life_hours: number | null; + annual_standard_hours: number | null; + depreciation_coefficient: number | null; + maintenance_coefficient: number | null; + management_coefficient: number | null; + loss_coefficient: number | null; + loss_krw_per_hour: string | null; + fuel_liters_per_hour: string | null; + fuel_price_per_liter: string | null; + fuel_scope: string; + misc_material_percent: string | null; + operator_code: string; + operator_daily_wage: string | null; + operator_krw_per_hour: string | null; + material_krw: string | null; + labor_krw: string | null; + expense_krw: string | null; + total_krw: string | null; + variant: string; + attachment: boolean; + attachment_note: string; + gaps: string[]; + }>; +} + +export async function fetchMachineExpense(projectId: string): Promise { + const response = await fetch( + `${API_BASE_URL}/projects/${encodeURIComponent(projectId)}/estimation/machine-expense`, + { credentials: "include" }, + ); + if (!response.ok) throw new Error(`machine-expense ${response.status}`); + return (await response.json()) as MachineExpenseDto; +} + +/** 기종 한 장 — 손료·운전경비·시간당 사용료를 차례로. */ +function machineExpenseSheet(sheet: MachineExpenseDto["sheets"][number]): HTMLElement { + const box = el("div", "b09s-group"); + box.append( + el( + "div", + "b09s-head", + `${sheet.machine_code} ${sheet.name} ${sheet.spec}`.trim() + + (sheet.variant ? ` — ${sheet.variant}` : ""), + ), + ); + const coefficient = (value: number | null) => (value === null ? "—" : String(value)); + box.append( + infoTable( + ["구 분", "내 용", "값"], + [ + ["① 손료", "취득가격(천원)", money(sheet.price_thousand_krw)], + [ + "", + "내용시간 / 연간표준가동시간", + `${coefficient(sheet.economic_life_hours)} / ${coefficient(sheet.annual_standard_hours)}`, + ], + [ + "", + "상각비·정비비·관리비 계수 (10⁻⁷)", + `${coefficient(sheet.depreciation_coefficient)} + ${coefficient(sheet.maintenance_coefficient)} + ${coefficient(sheet.management_coefficient)} = ${coefficient(sheet.loss_coefficient)}`, + ], + ["", "시간당 손료(원)", money(sheet.loss_krw_per_hour)], + [ + "② 운전경비", + `주연료(L/hr) × 유가(${sheet.fuel_scope})`, + `${sheet.fuel_liters_per_hour ?? "—"} × ${money(sheet.fuel_price_per_liter)}`, + ], + ["", "잡재료(주연료의 %)", sheet.misc_material_percent ?? "—"], + [ + "", + `조종원(${sheet.operator_code || "—"}) 일당 → 시간당`, + `${money(sheet.operator_daily_wage)} → ${money(sheet.operator_krw_per_hour)}`, + ], + [ + "③ 시간당 사용료", + "재료비 / 노무비 / 경비", + `${money(sheet.material_krw)} / ${money(sheet.labor_krw)} / ${money(sheet.expense_krw)}`, + ], + ["", "합 계", money(sheet.total_krw)], + ], + [0, 1], + ), + ); + if (sheet.variant) { + box.append( + note( + "같은 기종이라도 조합 사용이면 잡재료가 16% 로 줄어 재료비가 달라집니다 —" + + " 그래서 층이 따로 섭니다(건설품셈 제8장 [주]⑤).", + ), + ); + } + if (sheet.attachment_note) box.append(note(sheet.attachment_note)); + for (const gap of sheet.gaps) box.append(note(`⚠ ${gap}`)); + return box; +} + +export function drawMachineExpense(body: HTMLElement, data: MachineExpenseDto): void { + body.append(head(`각종 중기경비계산서 (${data.sheets.length})`)); + body.append(note(data.summary)); + for (const line of data.notes) body.append(note(line)); + for (const sheet of data.sheets) body.append(machineExpenseSheet(sheet)); +} diff --git a/B09_Estimation/B09_Estimation_UI_Tab_Machine.ts b/B09_Estimation/B09_Estimation_UI_Tab_Machine.ts index df15a90d..9c68203e 100644 --- a/B09_Estimation/B09_Estimation_UI_Tab_Machine.ts +++ b/B09_Estimation/B09_Estimation_UI_Tab_Machine.ts @@ -5,12 +5,12 @@ * - 목록표 = 내역이 쓴 시간당 중기만, 처음 쓰인 차례(서버 `lists.machine`). * - 줄을 누르면 아래에 그 기종의 중기사용료 호표 — 손료(경비) · 운전원(노무) · 주연료(재료) · 잡품(재료). * ⚠ 잡품은 「주연료비 × 율」 가산 행 — 수량 칸에 율(%), 단가 칸에 밑수(주연료비)가 옴(실무 표 그대로). - * - 그 아래 중기경비계산서(취득가·손료계수·연료·조종원 과정)는 옛 표를 그대로 이어 보임. + * - 그 아래 중기경비계산서(취득가·손료계수·연료·조종원 과정) — _UI_MachineExpense. * ========================================================================== */ import type { B09Tab, B09TabContext } from "./B09_Estimation_UI_Shell_Types"; import { renderDetail } from "./B09_Estimation_UI_Detail"; -import { drawMachineExpense, fetchMachineExpense } from "./B09_Estimation_UI_BaseData"; +import { drawMachineExpense, fetchMachineExpense } from "./B09_Estimation_UI_MachineExpense"; import { L, el, hint, numberCell, plainTable, won } from "./B09_Estimation_UI_Sheet"; import { loadBill, type BillDto } from "./B09_Estimation_UI_Store"; @@ -62,7 +62,7 @@ function draw(ctx: B09TabContext, bill: BillDto): void { detail.append(hint(L("B09_Sheet_PickSheet"))); } if (ctx.projectId) { - // 중기경비계산서 — 「그 값이 왜 나왔나」(옛 표 그대로, 옛 파일을 걷을 때 함께 옮김). + // 중기경비계산서 — 「그 값이 왜 나왔나」(옛 표를 옮겨 온 _UI_MachineExpense). void fetchMachineExpense(ctx.projectId) .then((data) => drawMachineExpense(expense, data)) .catch(() => expense.append(hint(L("B09_Sheet_LoadFailed"), true)));