feat(B09): 산출기초 — 줄에 달린 근거를 한 장으로 접음
별표2 (5)(가) 열셋째. 근거는 줄마다 이미 있었고 묶는 자리만 없었음. 원가계산에 「산출기초」 탭으로 세움. 넷으로 접음 — ① 어느 판으로 계산했나(데이터 기준일·지문) ② 무엇을 골랐나(고른 값만) ③ 공종마다 무엇을 근거로 했나(줄 문구 그대로) ④ 못 채운 자리(0 으로 안 때운 자리). - 판 목록은 장부(_manifest.json)를 그대로 읽음 — 목록을 따로 적으면 한쪽만 고쳐짐. - ⚠ 여기서 값을 다시 계산하지 않음 — 금액 칸이 아예 없음(두 벌 방지, 시험으로 못 박음). ⇒ 「설계서 구성」의 산출기초가 반쪽 → 있음. 법이 정한 13 중 9 가 서고, 우리가 더 낼 것은 공사설명서 하나(서식·설계하중 표기는 사용자에게 받아야 함). 곁들여: 자재총괄의 「미분류」를 「미정(발주기관 결정)」으로 고침 — 발주기관이 정할 자리인데 우리가 못 만든 것처럼 읽히던 문구(V-14). 값·판정은 그대로. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -357,6 +357,91 @@ export function drawMachineExpense(body: HTMLElement, data: MachineExpenseDto):
|
||||
for (const sheet of data.sheets) body.append(machineExpenseSheet(sheet));
|
||||
}
|
||||
|
||||
/* =============================================================================
|
||||
* 산출기초 — 줄에 달린 근거를 한 장으로 모은 장(별표2 (5)(가) 열셋째).
|
||||
* ⚠ 여기서 값을 다시 계산하지 않는다 — 모으기만 한다.
|
||||
* ========================================================================== */
|
||||
|
||||
export interface BasisSheetDto {
|
||||
status: string;
|
||||
note: string;
|
||||
summary: string;
|
||||
dataset_versions: Array<{
|
||||
dataset_id: string;
|
||||
file: string;
|
||||
effective_date: string;
|
||||
sha256: string;
|
||||
}>;
|
||||
chosen_conditions: Array<{ item: string; value: string }>;
|
||||
work_items: Array<{ code: string; name: string; unit: string; notes: string[] }>;
|
||||
gaps: Array<{ kind: string; code: string; reason: string }>;
|
||||
}
|
||||
|
||||
export async function fetchBasisSheet(projectId: string): Promise<BasisSheetDto> {
|
||||
const response = await fetch(
|
||||
`${API_BASE_URL}/projects/${encodeURIComponent(projectId)}/estimation/basis-sheet`,
|
||||
{ credentials: "include" },
|
||||
);
|
||||
if (!response.ok) throw new Error(`basis-sheet ${response.status}`);
|
||||
return (await response.json()) as BasisSheetDto;
|
||||
}
|
||||
|
||||
export function drawBasisSheet(body: HTMLElement, data: BasisSheetDto): void {
|
||||
body.append(head("산출기초"));
|
||||
body.append(note(data.summary));
|
||||
body.append(note(data.note));
|
||||
|
||||
body.append(head(`① 어느 판으로 계산했나 (${data.dataset_versions.length})`));
|
||||
body.append(
|
||||
table(
|
||||
["자료", "파일", "기준일", "지문(앞 12)"],
|
||||
data.dataset_versions.map((row) => [
|
||||
row.dataset_id,
|
||||
row.file,
|
||||
row.effective_date,
|
||||
row.sha256 || "—",
|
||||
]),
|
||||
[0, 1, 2, 3],
|
||||
),
|
||||
);
|
||||
|
||||
body.append(head(`② 무엇을 골랐나 (${data.chosen_conditions.length})`));
|
||||
if (data.chosen_conditions.length === 0) {
|
||||
body.append(note("고른 값이 없습니다 — 전부 확정 기본값으로 돌고 있습니다."));
|
||||
} else {
|
||||
body.append(
|
||||
table(
|
||||
["항 목", "고른 값"],
|
||||
data.chosen_conditions.map((row) => [row.item, row.value]),
|
||||
[0, 1],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
body.append(head(`③ 공종마다 무엇을 근거로 했나 (${data.work_items.length})`));
|
||||
body.append(
|
||||
table(
|
||||
["코드", "공 종", "단위", "근 거"],
|
||||
data.work_items.map((row) => [row.code, row.name, row.unit, row.notes.join(" · ")]),
|
||||
[0, 1, 2, 3],
|
||||
),
|
||||
);
|
||||
|
||||
body.append(head(`④ 못 채운 자리 (${data.gaps.length})`));
|
||||
if (data.gaps.length === 0) {
|
||||
body.append(note("못 채운 자리가 없습니다."));
|
||||
} else {
|
||||
body.append(
|
||||
table(
|
||||
["갈 래", "코드", "사 유"],
|
||||
data.gaps.map((row) => [row.kind, row.code, row.reason]),
|
||||
[0, 1, 2],
|
||||
),
|
||||
);
|
||||
body.append(note("⚠ 여기 있는 것은 0 으로 때우지 않고 남겨 둔 자리입니다."));
|
||||
}
|
||||
}
|
||||
|
||||
/** 두 탭이 함께 쓰는 「아직 못 불러왔습니다」 문구. */
|
||||
export function drawBaseDataError(body: HTMLElement): void {
|
||||
body.append(note(L("B09_Estimation_Tab_Pending")));
|
||||
|
||||
Reference in New Issue
Block a user