- 일위대가목록 = 구조물 조합 + 사전조합 + 일위대가화(승격 · 옛 SP) 줄 · 단가산출 줄 = 산근 줄 - 산근 줄 수량 = 수량 그대로(배 ≠ 1 로직 금액 1/배 흠) · 대가 줄만 ÷ 배 - 품셈 블록 로직 줄 판정 = 산근 풀이와 같은 call_of(기계경비 = 중기 · 그 밖 = 하위 산근) - 비고 「산근 n호표」(Calc_Labor 파이썬 · TS 짝) · 산근 줄 누름 = 단가산출근거 탭 - 산근목록 원천 = 단가산출 줄 + 사전조합 안 줄 + 일위대가화 줄 하위 로직(노무 · 자재 · 중기 줄 제외) - 산근 자재 자리표시 키(MO:경유:…) = 자재단가대비표 키로 - 시험: 145-1 다섯 · 145-2 셋 새 갈래로(머리에 근거 한 줄) · 새 test_155_b09_sources Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015KYZ2HWWdUwZWYnn3tQz7X
134 lines
4.6 KiB
TypeScript
134 lines
4.6 KiB
TypeScript
/* =============================================================================
|
||
* B09_Estimation_UI_Tabs_Labor.ts
|
||
* B09 대가 탭 둘(PLAN 145-2) — 일위대가목록표 · 일위대가표(실무 엑셀 꼴 · 값 = `B09_Estimation_Calc_Labor`).
|
||
*
|
||
* 목록표 = 호표 한 줄(합계 · 노무비 · 재료비 · 경비) · 줄 누름 = 그 블록으로
|
||
* 일위대가표 = 블록(머리 · [품셈 인용] · 줄 · 합계) · 줄 누름 = 단가를 낸 탭 · 줄(대가 줄 = 그 블록)
|
||
* 줄 꼴(155-2) = 머리 · 합계 굵게(`is-title`) · 인용 · 줄 = 들여쓰기 「ㄴ」(2단 · 겹 대가는 안 펼침)
|
||
* 연결된 구조물 조합 사본이 없으면 빈 표 「연결 없음」
|
||
* ========================================================================== */
|
||
|
||
import type { DataRow } from "@ui/ui_template_data_table";
|
||
import type { Row } from "./B09_Estimation_Calc_Base";
|
||
import {
|
||
money,
|
||
qty,
|
||
text,
|
||
type Link,
|
||
type TabBuild,
|
||
type TabId,
|
||
type TabTable,
|
||
} from "./B09_Estimation_UI_Tabs";
|
||
|
||
const EMPTY =
|
||
"연결 없음 — 연결된 구조물 조합 사본(일위대가)이 없음 · B05 · B06 에서 조합을 고르고 [저장]";
|
||
/** 하위 줄 앞 — 들여쓰기 + 「ㄴ」(공용 표가 앞 빈칸을 접지 않음) */
|
||
const CHILD = " ㄴ ";
|
||
const ROUNDS = "줄 금액 ROUNDDOWN(단가 × 수량, 1) · % 줄 = 밑수 × % / 100 · 합계 ROUNDDOWN 0";
|
||
|
||
/** 블록 줄 → 단가를 낸 탭 */
|
||
const LINE_TAB: Record<string, TabId> = {
|
||
labor: "labor",
|
||
material: "material",
|
||
machine: "machine",
|
||
daega: "dg",
|
||
sangeun: "sg",
|
||
};
|
||
|
||
export const buildDaegaList: TabBuild = ({ sheets }): TabTable | string => {
|
||
const rows: DataRow[] = (sheets.daega || []).map((r: Row) => ({ ...r, no: `대가${r.no}` }));
|
||
if (!rows.length) return EMPTY;
|
||
return {
|
||
columns: [
|
||
text("no", "호표", 80),
|
||
text("kind", "꼴", 70),
|
||
text("name", "명칭", 240),
|
||
text("spec", "규격", 200),
|
||
text("unit", "단위", 56),
|
||
money("sum", "합계"),
|
||
money("labor", "노무비"),
|
||
money("material", "재료비"),
|
||
money("expense", "경비"),
|
||
],
|
||
rows,
|
||
frozen: 3,
|
||
hint: "호표 = 일위대가표 블록 합계 · 줄을 누르면 그 블록으로",
|
||
link: (i: number): Link | null => (rows[i] ? { tab: "dg", key: String(rows[i].key) } : null),
|
||
};
|
||
};
|
||
|
||
export const buildDaegaBlocks: TabBuild = ({ sheets }): TabTable | string => {
|
||
const blocks: Row[] = sheets.daega_blocks || [];
|
||
if (!blocks.length) return EMPTY;
|
||
const rows: DataRow[] = [];
|
||
const links: Array<Link | null> = [];
|
||
const titles = new Set<number>();
|
||
for (const b of blocks) {
|
||
titles.add(rows.length);
|
||
rows.push({
|
||
key: b.id,
|
||
name: `제 ${b.no} 호표 ${b.name}`,
|
||
spec: b.spec,
|
||
unit: b.unit,
|
||
note: b.kind,
|
||
});
|
||
links.push(null);
|
||
if (b.cite) {
|
||
rows.push({ key: `${b.id}|cite`, name: `${CHILD}${b.cite}`, note: "품셈 인용" });
|
||
links.push(null);
|
||
}
|
||
b.lines.forEach((ln: Row, n: number) => {
|
||
rows.push({
|
||
key: `${b.id}|${n}`,
|
||
name: `${CHILD}${ln.name || ""}`,
|
||
spec: ln.spec || "",
|
||
qty: ln.qty,
|
||
unit: ln.unit || "",
|
||
sum_price: ln.price,
|
||
sum: ln.amount,
|
||
labor_price: ln.prices.labor,
|
||
labor: ln.amounts.labor,
|
||
material_price: ln.prices.material,
|
||
material: ln.amounts.material,
|
||
expense_price: ln.prices.expense,
|
||
expense: ln.amounts.expense,
|
||
note: ln.ref_no || ln.why || "",
|
||
});
|
||
const tab = LINE_TAB[ln.kind];
|
||
links.push(tab && ln.key ? { tab, key: String(ln.key) } : null);
|
||
});
|
||
titles.add(rows.length);
|
||
rows.push({
|
||
key: `${b.id}|합계`,
|
||
name: " 합 계",
|
||
sum: b.sum,
|
||
labor: b.labor,
|
||
material: b.material,
|
||
expense: b.expense,
|
||
});
|
||
links.push(null);
|
||
}
|
||
const pair = (key: string, label: string) => [
|
||
money(`${key}_price`, "단가", [label], 1),
|
||
money(key, "금액", [label], 1),
|
||
];
|
||
return {
|
||
columns: [
|
||
text("name", "명칭", 240),
|
||
text("spec", "규격", 160),
|
||
qty("qty", "수량"),
|
||
text("unit", "단위", 56),
|
||
...pair("sum", "합계"),
|
||
...pair("labor", "노무비"),
|
||
...pair("material", "재료비"),
|
||
...pair("expense", "경비"),
|
||
text("note", "비고", 120),
|
||
],
|
||
rows,
|
||
frozen: 1,
|
||
hint: `단가 = B09 프로젝트 한 벌 굳힌 단가 · ${ROUNDS} · 줄 누름 = 단가를 낸 탭(대가 줄 = 그 블록)`,
|
||
link: (i: number) => links[i] ?? null,
|
||
rowClass: (i: number) => (titles.has(i) ? "is-title" : null),
|
||
};
|
||
};
|