refactor(b09): 옛 탭 옮기기 ③ 관급·사급 — 새 틀 탭 파일로(내역 응답 한 벌을 봄)
- 화면 확인: 옮기기 전후 같음 — 사급 0 · 관급 0 · 안 갈린 것 13줄 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
This commit is contained in:
@@ -25,6 +25,7 @@ import { summaryTab } from "./B09_Estimation_UI_Tab_Summary";
|
||||
import { listsTab } from "./B09_Estimation_UI_Tab_Lists";
|
||||
import { designDocTab } from "./B09_Estimation_UI_Tab_DesignDoc";
|
||||
import { basisSheetTab } from "./B09_Estimation_UI_Tab_BasisSheet";
|
||||
import { supplyTab } from "./B09_Estimation_UI_Tab_Supply";
|
||||
|
||||
/** 탭 등록 — 한 줄에 탭 하나. 옛 탭(`legacyTab`)은 새 탭 파일이 서면 그 줄만 바꿈. */
|
||||
const TABS: B09Tab[] = [
|
||||
@@ -37,7 +38,7 @@ const TABS: B09Tab[] = [
|
||||
priceCompareTab,
|
||||
summaryTab,
|
||||
listsTab,
|
||||
legacyTab("supply", "B09_Estimation_Tab_Supply"),
|
||||
supplyTab,
|
||||
legacyTab("base_data", "B09_Estimation_Tab_BaseData"),
|
||||
designDocTab,
|
||||
basisSheetTab,
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
* ========================================================================== */
|
||||
|
||||
import { API_BASE_URL } from "@config/config_frontend";
|
||||
import type { ProvenancePayload } from "@ui/ui_template_provenance";
|
||||
|
||||
export interface BillNoteDto {
|
||||
column: string;
|
||||
@@ -89,7 +90,28 @@ export interface ResourceRowDto {
|
||||
|
||||
export type ResourceGroup = "labor" | "material" | "expense" | "machine" | "lumpsum";
|
||||
|
||||
export interface MaterialSheetRowDto {
|
||||
name: string;
|
||||
spec: string;
|
||||
unit: string;
|
||||
total_amount: string;
|
||||
unit_price_krw: string | null;
|
||||
amount_krw: string | null;
|
||||
note: string;
|
||||
}
|
||||
|
||||
export interface MaterialSheetDto {
|
||||
contractor: MaterialSheetRowDto[];
|
||||
owner: MaterialSheetRowDto[];
|
||||
unknown: MaterialSheetRowDto[];
|
||||
contractor_total_krw: string;
|
||||
owner_total_krw: string;
|
||||
notes: string[];
|
||||
}
|
||||
|
||||
export interface BillDto {
|
||||
/** 근거 사전 — 개발환경에서만 옴. */
|
||||
provenance?: ProvenancePayload;
|
||||
resources: {
|
||||
groups: Record<ResourceGroup, ResourceRowDto[]>;
|
||||
missing: string[];
|
||||
@@ -104,6 +126,7 @@ export interface BillDto {
|
||||
missing: MissingDto[];
|
||||
unconfirmed_count: number;
|
||||
notes: string[];
|
||||
material_sheet: MaterialSheetDto | null;
|
||||
};
|
||||
price_basis: { entries: SheetEntryDto[] };
|
||||
unit_price_sheet: { entries: SheetEntryDto[] };
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/* =============================================================================
|
||||
* B09_Estimation_UI_Tab_Supply.ts
|
||||
* B09 관급·사급 탭 — 자재대: B08 수량·할증에 단가를 붙인 표 (PLAN 8-7 「금액은 B09」)
|
||||
*
|
||||
* - 옛 `B09_Estimation_UI_Page.ts` 의 자재대 탭을 그대로 옮김(PLAN 12장 옛 탭 옮기기).
|
||||
* - 사급(도급 재료비) · 관급(총원가 밖 별도 표기) · 안 갈린 것(어느 합계에도 안 넣음) 세 무리.
|
||||
* ⚠ 「안 갈린 것」은 못 세운 것이 아니라 **세면 안 되는 것** — 채우면 이중계상(PLAN 8-36 ㉱).
|
||||
* - 자료는 설계내역서 응답 한 벌(`summary.material_sheet`) — 내역 탭들과 같은 응답을 봄.
|
||||
* ========================================================================== */
|
||||
|
||||
import type { ui_locales } from "@ui/ui_template_locale";
|
||||
import type { B09Tab } from "./B09_Estimation_UI_Shell_Types";
|
||||
import { L, hint, quantity } from "./B09_Estimation_UI_Sheet";
|
||||
import { infoTable, note } from "./B09_Estimation_UI_Table";
|
||||
import { loadBill, type BillDto, type MaterialSheetRowDto } from "./B09_Estimation_UI_Store";
|
||||
|
||||
const MATERIAL_KEYS = [
|
||||
"name",
|
||||
"spec",
|
||||
"unit",
|
||||
"total_amount",
|
||||
"unit_price_krw",
|
||||
"amount_krw",
|
||||
"note",
|
||||
];
|
||||
|
||||
function draw(body: HTMLElement, bill: BillDto): void {
|
||||
const sheet = bill.summary.material_sheet;
|
||||
if (!sheet) {
|
||||
body.append(hint(L("B09_Estimation_Mat_Empty")));
|
||||
return;
|
||||
}
|
||||
// 자재가 아예 없으면 **빈 표 셋을 늘어놓지 않음** — 「없다」 한 줄이면 됨(2026-09-08 화면 전수).
|
||||
if (sheet.contractor.length === 0 && sheet.owner.length === 0 && sheet.unknown.length === 0) {
|
||||
body.append(hint(L("B09_Estimation_Mat_None")));
|
||||
return;
|
||||
}
|
||||
const groups: Array<[keyof typeof ui_locales, MaterialSheetRowDto[], string | null, string]> = [
|
||||
["B09_Estimation_Mat_Contractor", sheet.contractor, sheet.contractor_total_krw, "material"],
|
||||
["B09_Estimation_Mat_Owner", sheet.owner, sheet.owner_total_krw, "material"],
|
||||
["B09_Estimation_Mat_Unknown", sheet.unknown, null, "material_unknown"],
|
||||
];
|
||||
for (const [labelKey, rows, total, sheetName] of groups) {
|
||||
body.append(note(`${L(labelKey)} (${rows.length})` + (total === null ? "" : ` — ${total}`)));
|
||||
if (rows.length === 0) continue;
|
||||
body.append(
|
||||
infoTable(
|
||||
["자재", "규격", "단위", "수량", "단가", "금액", "비고"],
|
||||
rows.map((row) => [
|
||||
row.name,
|
||||
row.spec,
|
||||
row.unit,
|
||||
quantity(row.total_amount, 2),
|
||||
row.unit_price_krw ?? "",
|
||||
row.amount_krw ?? "",
|
||||
row.note,
|
||||
]),
|
||||
[0, 1, 2, 6],
|
||||
MATERIAL_KEYS,
|
||||
bill.provenance?.sheets?.[sheetName],
|
||||
),
|
||||
);
|
||||
}
|
||||
for (const line of sheet.notes) body.append(note(line.replace(/\*\*/g, "")));
|
||||
}
|
||||
|
||||
export const supplyTab: B09Tab = {
|
||||
key: "supply",
|
||||
label: () => L("B09_Estimation_Tab_Supply"),
|
||||
render(ctx) {
|
||||
if (!ctx.projectId) {
|
||||
ctx.body.append(hint(L("B09_Sheet_NoProject")));
|
||||
return;
|
||||
}
|
||||
ctx.body.append(hint(L("B09_Sheet_Loading")));
|
||||
loadBill(ctx.projectId)
|
||||
.then((bill) => {
|
||||
ctx.body.replaceChildren();
|
||||
draw(ctx.body, bill);
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
ctx.body.replaceChildren(hint(`${L("B09_Sheet_LoadFailed")} ${error.message}`, true));
|
||||
});
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user