- 서버 Sangeun_Calc: 배 = 바탕 기준단위 → SK 단위(master_unit.swap) · 성격 다름 = 문서 환산 {계수 | 단위중량} · 없으면 셈 · 저장 막음(까닭 글) · 「식」 · 빈 단위 = 옛 셈 그대로(일위대가 줄 모드)
- 화면 셈 답 줄 · 계 = SK 1 단위 몫(per_unit · 화면배) · 머리 띠 「100본 당」 · 「1 본 당」
- 서버 Sangeun_Doc: 환산 칸 검사(계수 양수 · 단위중량 종별 · 형상)
- B09 Labor_Source: SK 참조 배 = 같은 ratio_of(옛 바탕 앞 수 대신 · 기존 문서 같은 값)
- 화면 Sangeun_Detail: SK 단위 처음 값 = 기초 기준단위(「100본」) · 둘째 기초 수량 처음값 = 바탕 기준단위 ÷ 그 기초 기준단위(swapUnit) · 계 칸 원/SK 단위
- 새 Sangeun_Unit: 근거 글 · 환산 계수 칸 · 단위중량 표(CC000337) 고르기
- Cover_Model: 화면 품 × 화면배 = 문서 품(덮개 · 더한줄 · 세부) · 율 그대로
- Find_Pick: 돈 아닌 기초(결과단위 「원」 아님 21 개) 목록에서 뺌
- 낡은 시험 test_715_sangeun_engine(바탕 원/주 + SK ㎡) · test_727(바탕 원/㎡ + SK ㎥) = 성격 다른 단위가 이제 계수 없으면 막혀 SK 단위를 바탕 단위로 고침(금액 같음)
- 새 시험 test_728_2_sk_unit(100본 → 본 1,538.89 · ㎡ 44,791.32 · 계수 · 단위중량 · 식 · 둘째 기초 · 돈 아닌 기초 · 고침 꼴)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
104 lines
4.3 KiB
TypeScript
104 lines
4.3 KiB
TypeScript
/* =============================================================================
|
|
* M02_MasterTemplete_Sangeun_Unit.ts
|
|
* 단가산출(SK) 단위 환산 칸(PLAN 728-2 · 요구서_내역19 3장 5) — 바탕 기준단위(「100본」) → SK 단위 근거 글 ·
|
|
* 같은 성격 = 배 자동(글만) · 성격 다름 = 환산 계수 칸 · 부피 ↔ 무게 = 단위중량 표(CC000337) 고르기.
|
|
* 칸 보임 · 근거 글 = `swapUnit`(짝 `A00_Common/master_unit_swap.ts`) · 셈 = 서버(`M02_Sangeun_Calc.ratio_of`)
|
|
* 문서 칸 `환산` {계수 | 단위중량 {종별, 형상}}(꼴 = 서버 `M02_Sangeun_Doc._swap_how`) · 바꿈 = 캐시 → [저장].
|
|
* ========================================================================== */
|
|
|
|
import { el } from "@ui/ui_template_elements";
|
|
import { createInputField, createSelectField } from "@ui/ui_template_input";
|
|
import { t as L } from "@ui/ui_template_locale";
|
|
import { createText } from "@ui/ui_template_text";
|
|
import { swapUnit } from "../A00_Common/master_unit_swap";
|
|
import { loadTable } from "../M01_MasterData/M01_MasterData_UI_Logic_Note";
|
|
|
|
export interface SwapHow {
|
|
계수?: number;
|
|
단위중량?: { 종별: string; 형상: string };
|
|
}
|
|
|
|
const WEIGHT_TABLE = "CC000337";
|
|
|
|
/** 결과단위 「원/100본」 → 기준단위 「100본」(SK 단위 처음 값) */
|
|
export const baseUnitOf = (result: string): string => result.replace(/^원\s*\/\s*/, "").trim();
|
|
|
|
/** 단위중량 표 줄 → ㎏/㎥(위 끝 · 서버 `master_unit.unit_weight` 기본과 같음) */
|
|
const rhoOf = (row: Record<string, unknown> | undefined): number | null =>
|
|
row && Number.isFinite(Number(row.위)) ? Number(row.위) : null;
|
|
|
|
/** 환산 칸 — 「식」 · 빈 단위 · 바탕 없음 = null(옛 셈) */
|
|
export function buildUnitSwap(opt: {
|
|
result: string;
|
|
unit: string;
|
|
how: SwapHow | undefined;
|
|
edit: boolean;
|
|
onChange: (how: SwapHow | undefined) => void;
|
|
}): HTMLElement | null {
|
|
const have = baseUnitOf(opt.result);
|
|
if (!have || !opt.unit || opt.unit === "식") return null;
|
|
const how = opt.how ?? {};
|
|
const probe = swapUnit(have, opt.unit, 1000, how.계수);
|
|
const root = el("div", { className: "m02-sk__tags", attrs: { "data-sk-swap": "" } });
|
|
const note = createText("", "muted", 14);
|
|
const say = (rho: number | null): void => {
|
|
const got = swapUnit(have, opt.unit, rho, how.계수);
|
|
note.textContent = got.근거;
|
|
note.dataset.skSwapBlocked = got.배 === null ? "1" : "";
|
|
};
|
|
if (probe.계수 === "사용자") {
|
|
const [, ua] = probe.가;
|
|
const [, ub] = probe.나;
|
|
const box = createInputField({
|
|
label: `${L("M02_SangeunSwapCoef")} — 1 ${ua} = ? ${ub}`,
|
|
type: "number",
|
|
value: how.계수 != null ? String(how.계수) : "",
|
|
min: 0,
|
|
compact: true,
|
|
size: 28,
|
|
});
|
|
box.input.dataset.skSwapCoef = "";
|
|
box.input.disabled = !opt.edit;
|
|
box.input.addEventListener("change", () => {
|
|
const v = Number(box.input.value);
|
|
opt.onChange(Number.isFinite(v) && v > 0 ? { 계수: v } : undefined);
|
|
});
|
|
root.append(box.root);
|
|
} else if (probe.계수 === "단위중량") {
|
|
const pick = how.단위중량;
|
|
const value = pick ? `${pick.종별}|${pick.형상}` : "";
|
|
const select = createSelectField({
|
|
label: `${L("M02_SangeunSwapWeight")} (${WEIGHT_TABLE})`,
|
|
options: [{ value: "", text: L("M02_SangeunSwapPick") }],
|
|
value,
|
|
compact: true,
|
|
size: 28,
|
|
disabled: !opt.edit,
|
|
onChange: (v) => {
|
|
const [종별, 형상] = v.split("|");
|
|
opt.onChange(v ? { 단위중량: { 종별, 형상 } } : undefined);
|
|
},
|
|
});
|
|
select.select.dataset.skSwapWeight = "";
|
|
root.append(select.root);
|
|
void loadTable(WEIGHT_TABLE).then((table) => {
|
|
const rows = table?.줄 ?? [];
|
|
select.setOptions(
|
|
[
|
|
{ value: "", text: L("M02_SangeunSwapPick") },
|
|
...rows.map((r) => ({
|
|
value: `${String(r.종별)}|${String(r.형상)}`,
|
|
text: `${String(r.종별)} · ${String(r.형상)} (${String(r.위)}㎏/㎥)`,
|
|
})),
|
|
],
|
|
value,
|
|
);
|
|
say(rhoOf(rows.find((r) => r.종별 === pick?.종별 && r.형상 === pick?.형상)));
|
|
});
|
|
}
|
|
say(null);
|
|
if (!root.childElementCount && !note.textContent) return null;
|
|
root.append(note);
|
|
return root;
|
|
}
|