- UI_Tabs_Bill — 항목 수량 덮어쓰기(빈칸 = 자동 · 풍선 자동 값) · 손 줄 명칭 · 규격 · 단위 · 수량 - UI_Shell — 내역서 표 우클릭 메뉴 붙임 · 고친 뒤 보던 자리 지킴 - Calc_Bill 짝 — 손 줄 자리(after) 놓침 고침 - 손 줄 띠 표시(CSS) · 새 시험 test_166_2(셈 · 거울 · 다듬기 · 새 산근 · 집계표 · 메뉴) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015KYZ2HWWdUwZWYnn3tQz7X
82 lines
3.7 KiB
TypeScript
82 lines
3.7 KiB
TypeScript
/* =============================================================================
|
|
* B09_Estimation_UI_Tabs_Bill.ts
|
|
* B09 설계내역서 탭(PLAN 145-3) — 실무 엑셀 꼴(공종 · 명칭 · 규격 · 수량 · 단위 · 합계 · 노무 · 재료 · 경비 · 비고).
|
|
*
|
|
* 값 = `B09_Estimation_Calc_Bill`(조작 중 브라우저 · [저장] 때 서버 정본)
|
|
* 고칠 칸 = 관급자재대 조달수수료 율(%) · 항목 수량(166-2 · 덮어쓰기 · 빈칸 = 자동) ·
|
|
* 손 줄 명칭 · 규격 · 단위 · 수량 · 빈칸 = 「연결 없음」(수량 · 단가 원천 없음)
|
|
* 줄 누름 = 단가를 낸 탭 줄(자재 → 재료비목록표 · 구조물 → 일위대가목록표)
|
|
* 우클릭 = 줄 추가 · 지우기 · 연결(`…_UI_Tabs_Bill_Edit`)
|
|
* ========================================================================== */
|
|
|
|
import type { DataRow } from "@ui/ui_template_data_table";
|
|
import type { Row } from "./B09_Estimation_Calc_Base";
|
|
import { money, parseNumber, qty, text, type TabBuild, type TabId } from "./B09_Estimation_UI_Tabs";
|
|
import {
|
|
billMenu,
|
|
editQty,
|
|
editText,
|
|
handEditable,
|
|
type BillTable,
|
|
} from "./B09_Estimation_UI_Tabs_Bill_Edit";
|
|
|
|
export const buildBill: TabBuild = (ctx): BillTable => {
|
|
const { sheets, user, setUser } = ctx;
|
|
const bill: Row = sheets.bill || { rows: [], totals: null };
|
|
const rows: DataRow[] = bill.rows.map((r: Row) => ({
|
|
...r,
|
|
name: r.kind === "title" ? r.name : ` ${r.name}`,
|
|
}));
|
|
const direct: Row = bill.totals?.direct || {};
|
|
const pair = (key: string, label: string) => [
|
|
money(`price_${key}`, "단가", [label]),
|
|
money(key, "금액", [label]),
|
|
];
|
|
return {
|
|
columns: [
|
|
text("name", "공종 · 명칭", 220),
|
|
text("spec", "규격", 160),
|
|
qty("qty", "수량", 3),
|
|
text("unit", "단위", 56),
|
|
...pair("sum", "합계"),
|
|
...pair("labor", "노무비"),
|
|
...pair("material", "재료비"),
|
|
...pair("expense", "경비"),
|
|
text("note", "비고", 200),
|
|
],
|
|
rows,
|
|
total: { name: "순공사비 계(관급 제외)", ...direct },
|
|
frozen: 1,
|
|
hint:
|
|
"공종 나무 = 엑셀 꼴 고정 · 금액 = ROUNDDOWN(단가 × 수량, 0) 비목마다 · 4 자재대 = 대비표 「별도」 · 관급자재대 = 「관급」 표시 자재 · 조달수수료 율(%)은 수량 칸에 넣음 · " +
|
|
"수량 칸 = 덮어쓰기(빈칸 = 자동) · 우클릭 = 줄 추가 · 지우기 · 일위대가 · 단가산출 연결(새 산근 단가 = [저장] 뒤) · " +
|
|
`빈 줄 ${direct.blank ?? 0} — 계는 아는 금액만 · 원가계산서는 빈 줄이 없을 때만 받음`,
|
|
editable: (i, key) => {
|
|
const r = rows[i];
|
|
if (!r || r.kind !== "item") return false;
|
|
return r.hand ? handEditable(key) : key === "qty";
|
|
},
|
|
onEdit: (i, key, value) => {
|
|
const r = rows[i];
|
|
if (!r) return;
|
|
if (r.hand && key !== "qty") return editText(ctx, String(r.key), key, value);
|
|
if (key !== "qty") return;
|
|
const v = parseNumber(value);
|
|
if (v === undefined) return;
|
|
if (r.key !== "fee") return editQty(ctx, r, v);
|
|
setUser({ ...user, estimate: { ...(user.estimate || {}), procurement_fee_pct: v } });
|
|
},
|
|
tip: (i, key) => {
|
|
const r = rows[i];
|
|
if (key !== "qty" || !r || !("qty_auto" in r)) return null;
|
|
return `자동 값 ${r.qty_auto ?? "빈칸"} — 우클릭 [수량 자동으로 되돌리기]`;
|
|
},
|
|
rowClass: (i) => (rows[i]?.hand ? "is-hand" : null),
|
|
menu: (i) => billMenu(ctx, rows, i),
|
|
link: (i) => {
|
|
const ref = rows[i]?.ref as Row | null | undefined;
|
|
return ref?.key ? { tab: ref.tab as TabId, key: String(ref.key) } : null;
|
|
},
|
|
};
|
|
};
|