Files
Aislo/B08_wf5_Quantity/B08_wf5_Quantity_UI_Tab_Basis.ts
T
2026-07-25 19:41:05 +09:00

181 lines
6.0 KiB
TypeScript

import { B08Api } from "./B08_wf5_Quantity_Api";
import { el, formData, input, section, select, table } from "./B08_wf5_Quantity_UI_Dom";
import { TabContext } from "./B08_wf5_Quantity_Types";
export function renderBasis(ctx: TabContext) {
const b = ctx.state.data.basis ?? {
project_id: ctx.state.projectId,
version: ctx.state.basisVersion,
base_date: "",
region: "",
currency: "KRW",
status: "DRAFT",
price_sources: [],
exchange_rates: [],
rate_policies: [],
};
const root = section("기준정보", "가격 출처·환율·제경비 요율을 버전 단위로 관리합니다.");
const meta = el("form", "b08-form-grid") as HTMLFormElement;
meta.append(
input("base_date", "기준일", "date", b.base_date ?? ""),
input("region", "적용 지역", "text", b.region ?? ""),
input("currency", "기준 통화", "text", b.currency ?? "KRW"),
select("status", "상태", [
["DRAFT", "작성중"],
["CONFIRMED", "확정"],
]),
);
(meta.elements.namedItem("status") as HTMLSelectElement).value = b.status;
const save = el("button", "b08-button b08-button--primary", "기준정보 저장") as HTMLButtonElement;
save.type = "submit";
meta.append(save);
meta.onsubmit = async (e) => {
e.preventDefault();
const x = formData(meta);
await B08Api.saveBasis(ctx.state.projectId, b.version, {
...b,
...x,
project_id: ctx.state.projectId,
version: b.version,
});
ctx.message("기준정보를 저장했습니다.");
await ctx.refresh();
};
root.append(meta);
root.append(renderSources(ctx, b), renderFx(ctx, b), renderPolicies(ctx, b));
return root;
}
function renderSources(ctx: TabContext, b: any) {
const box = section("가격 출처", "조달가격·물가정보·견적 등 후보단가의 출처와 우선순위입니다.");
const f = el("form", "b08-inline-form") as HTMLFormElement;
f.append(
input("source_code", "출처 코드"),
input("source_name", "출처명"),
input("priority_no", "우선순위", "number", "100"),
input("publisher", "발행기관"),
input("reference_date", "기준일", "date"),
);
const btn = el("button", "b08-button", "출처 추가") as HTMLButtonElement;
btn.type = "submit";
f.append(btn);
f.onsubmit = async (e) => {
e.preventDefault();
const x = formData(f);
b.price_sources.push({ ...x, priority_no: Number(x.priority_no) });
await B08Api.saveBasis(ctx.state.projectId, b.version, b);
await ctx.refresh();
};
box.append(
f,
table(
["코드", "출처", "발행기관", "우선순위", "기준일"],
b.price_sources.map((x: any) => [
x.source_code,
x.source_name,
x.publisher,
x.priority_no,
x.reference_date,
]),
),
);
return box;
}
function renderFx(ctx: TabContext, b: any) {
const box = section("환율", "외화 기초단가를 원화로 변환할 때 적용합니다.");
const f = el("form", "b08-inline-form") as HTMLFormElement;
f.append(
input("currency", "통화", "text"),
input("rate_to_krw", "원화환율", "number"),
select("source_id", "환율 출처", [
["", "직접 입력"],
...b.price_sources.map((x: any) => [String(x.id), x.source_name]),
]),
input("effective_from", "적용 시작일", "date"),
input("effective_to", "적용 종료일", "date"),
);
const btn = el("button", "b08-button", "환율 추가") as HTMLButtonElement;
btn.type = "submit";
f.append(btn);
f.onsubmit = async (e) => {
e.preventDefault();
const x = formData(f);
b.exchange_rates.push({
...x,
rate_to_krw: String(x.rate_to_krw),
source_id: x.source_id === "" ? null : Number(x.source_id),
effective_to: x.effective_to || null,
});
await B08Api.saveBasis(ctx.state.projectId, b.version, b);
await ctx.refresh();
};
box.append(
f,
table(
["통화", "원화환율", "출처", "시작일", "종료일"],
b.exchange_rates.map((x: any) => [
x.currency,
x.rate_to_krw,
b.price_sources.find((source: any) => source.id === x.source_id)?.source_name,
x.effective_from,
x.effective_to,
]),
),
);
return box;
}
function renderPolicies(ctx: TabContext, b: any) {
const box = section("요율 규칙", "승인된 규칙만 최종공사비 계산에 사용됩니다.");
const f = el("form", "b08-inline-form b08-policy-form") as HTMLFormElement;
f.append(
input("rule_code", "규칙 코드"),
input("rule_name", "규칙명"),
input("base_expression", "기준식"),
input("rate_value", "요율", "number"),
input("minimum_amount", "최소금액", "number"),
input("maximum_amount", "최대금액", "number"),
select("rounding_mode", "처리", [
["FLOOR", "절사"],
["ROUND", "반올림"],
["CEILING", "올림"],
]),
input("rounding_unit", "단위", "number", "1"),
input("source_reference", "근거 문서"),
select("status", "상태", [
["DRAFT", "작성중"],
["APPROVED", "승인"],
]),
);
const btn = el("button", "b08-button", "요율 추가") as HTMLButtonElement;
btn.type = "submit";
f.append(btn);
f.onsubmit = async (e) => {
e.preventDefault();
const x = formData(f);
b.rate_policies.push({
...x,
rate_value: x.rate_value || null,
rounding_unit: Number(x.rounding_unit),
minimum_amount: x.minimum_amount === "" ? null : Number(x.minimum_amount),
maximum_amount: x.maximum_amount === "" ? null : Number(x.maximum_amount),
condition_json: {},
sort_order: b.rate_policies.length,
});
await B08Api.saveBasis(ctx.state.projectId, b.version, b);
await ctx.refresh();
};
box.append(
f,
table(
["코드", "규칙", "기준식", "요율", "처리", "상태"],
b.rate_policies.map((x: any) => [
x.rule_code,
x.rule_name,
x.base_expression,
x.rate_value,
x.rounding_mode,
x.status,
]),
),
);
return box;
}