feat(b08): 자재총괄 할증률 「이내」 칸 — 표값 상한 안에서만 고름 · 비우면 표값(초안 연결은 Page 한 줄 대기)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn
This commit is contained in:
@@ -28,6 +28,8 @@ export interface MaterialRow {
|
||||
unit: string;
|
||||
net_amount: number;
|
||||
surcharge_pct: number | null;
|
||||
/** 할증률표 값 = 고를 수 있는 천장(품셈 1-3-1 「표의 값 이내」). `null` 이면 칸이 안 섬. */
|
||||
surcharge_cap?: number | null;
|
||||
total_amount: number;
|
||||
supply: string;
|
||||
supply_label: string;
|
||||
@@ -241,9 +243,49 @@ export interface SupplyChoice {
|
||||
export interface MaterialGridOptions {
|
||||
/** 저장 전 변경분 — 고른 값은 여기 쌓이고 [저장]에서만 정본으로 간다. */
|
||||
choices: Record<string, SupplyChoice>;
|
||||
/** 할증률 고른 값 `{이름 규격: % | null}` — 산출 조건 `material_surcharge`. 안 주면 칸 없이 글자만.
|
||||
* `null` = 표값으로 되돌림. 0 ~ 표값만 받음(서버도 한 번 더 거름). */
|
||||
surcharge?: Record<string, number | null>;
|
||||
onChange: () => void;
|
||||
}
|
||||
|
||||
/** 할증률 칸 — 표값(상한) 안에서만. 범위를 넘으면 칸이 빨갛게 되고 값을 안 받음. */
|
||||
function surchargeCell(
|
||||
row: MaterialRow,
|
||||
key: string,
|
||||
picks: Record<string, number | null>,
|
||||
onChange: () => void,
|
||||
): HTMLTableCellElement {
|
||||
const cap = row.surcharge_cap as number;
|
||||
const td = document.createElement("td");
|
||||
const input = document.createElement("input");
|
||||
input.type = "number";
|
||||
input.min = "0";
|
||||
input.max = String(cap);
|
||||
input.step = "0.1";
|
||||
input.className = "b08-grid__select";
|
||||
input.style.width = "4.5em";
|
||||
const picked = picks[key];
|
||||
input.value = String(picked ?? row.surcharge_pct ?? cap);
|
||||
input.title = `표값 ${cap}% 이내(품셈 1-3-1) · 비우면 표값`;
|
||||
input.addEventListener("change", () => {
|
||||
const text = input.value.trim();
|
||||
const value = Number(text);
|
||||
if (text !== "" && !(Number.isFinite(value) && value >= 0 && value <= cap)) {
|
||||
input.setCustomValidity(`0 ~ ${cap}% 만 받음`);
|
||||
input.reportValidity();
|
||||
return;
|
||||
}
|
||||
input.setCustomValidity("");
|
||||
picks[key] = text === "" ? null : value;
|
||||
if (text === "") input.value = String(cap);
|
||||
td.classList.add("is-changed");
|
||||
onChange();
|
||||
});
|
||||
td.append(input);
|
||||
return td;
|
||||
}
|
||||
|
||||
/** 표 안의 고르는 칸. 바꾼 줄은 **표시가 남는다** — 무엇을 만졌는지 보여야 한다. */
|
||||
function choiceCell(
|
||||
value: string,
|
||||
@@ -337,7 +379,12 @@ export function renderMaterialGrid(
|
||||
tr.append(textCell(row.unit, "b08-grid__unit"));
|
||||
tr.append(textCell(num(row.net_amount, 2)));
|
||||
// 미확보는 빈칸이 아니라 「-」 — 빈칸이면 0 % 로 오해된다.
|
||||
tr.append(textCell(row.surcharge_pct === null ? "-" : num(row.surcharge_pct, 0)));
|
||||
// 표값이 있는 줄은 「이내」로 고를 칸(초안 `surcharge` 가 이어졌을 때만).
|
||||
tr.append(
|
||||
options?.surcharge && typeof row.surcharge_cap === "number"
|
||||
? surchargeCell(row, key, options.surcharge, options.onChange)
|
||||
: textCell(row.surcharge_pct === null ? "-" : num(row.surcharge_pct, 0)),
|
||||
);
|
||||
tr.append(textCell(num(row.total_amount, 2)));
|
||||
if (options) {
|
||||
// 관급/사급은 **자재마다 갈리는 발주 결정**이라 줄에서 고른다(2026-09-07 확정).
|
||||
|
||||
Reference in New Issue
Block a user