feat(B09): 각종 중기경비계산서 — 기종마다 한 장
별표2 (5)(가) 아홉째. 중기목록표가 「얼마」라면 이 장은 「왜 그 값인가」임. 원가계산 → 중기 탭의 목록표 아래에 붙음. - 기종마다 취득가·내용시간·손료계수(상각·정비·관리) → 시간당 손료 · 주연료와 유가 · 조종원 일당과 시간당 환산 · 시간당 사용료 3분할을 차례로 보임. - 내역에 실제로 선 기종만 냄(카탈로그 613 을 다 뿌리지 않음). - 부착 장비(브레이커·콤팩터·집게)는 손료만 드는 것이 정상이라 결함으로 안 셈 — 제 엔진이 없어 연료·조종원이 본체에 듦(건설품셈 제8장 [주]⑤). - 수송비는 이 장에 안 붙임 — 「회당」으로 서는 별개 공종이라 자리만 가리킴. - 계산서 합계가 일위대가가 실제로 쓰는 값과 같은지 시험으로 못 박음(두 벌 방지). ⇒ 「설계서 구성」의 중기경비계산서가 반쪽 → 있음으로 바뀜(법이 정한 13 중 8 이 섬). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -240,6 +240,112 @@ export function drawDesignDocTab(body: HTMLElement, data: DesignDocDto): void {
|
||||
for (const line of data.notes) body.append(note(line));
|
||||
}
|
||||
|
||||
/* =============================================================================
|
||||
* 각종 중기경비계산서 — 기종마다 한 장(별표2 (5)(가) 아홉째).
|
||||
* ⚠ 목록표가 「얼마」라면 이 장은 **왜 그 값인가**다. 계산 과정을 감추지 않는다.
|
||||
* ========================================================================== */
|
||||
|
||||
export interface MachineExpenseDto {
|
||||
status: string;
|
||||
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;
|
||||
attachment: boolean;
|
||||
attachment_note: string;
|
||||
gaps: string[];
|
||||
}>;
|
||||
}
|
||||
|
||||
export async function fetchMachineExpense(projectId: string): Promise<MachineExpenseDto> {
|
||||
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 = document.createElement("div");
|
||||
box.className = "b09-panel__group";
|
||||
const title = document.createElement("p");
|
||||
title.className = "b09-panel__legend";
|
||||
title.textContent = `${sheet.machine_code} ${sheet.name} ${sheet.spec}`.trim();
|
||||
box.append(title);
|
||||
|
||||
const coefficient = (value: number | null) => (value === null ? "—" : String(value));
|
||||
box.append(
|
||||
table(
|
||||
["구 분", "내 용", "값"],
|
||||
[
|
||||
["① 손료", "취득가격(천원)", 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.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));
|
||||
}
|
||||
|
||||
/** 두 탭이 함께 쓰는 「아직 못 불러왔습니다」 문구. */
|
||||
export function drawBaseDataError(body: HTMLElement): void {
|
||||
body.append(note(L("B09_Estimation_Tab_Pending")));
|
||||
|
||||
Reference in New Issue
Block a user