- 문서 담은로직[].고름 {호표 차례: MT키} 정리 · 검사(재료 줄 · M01 자재품목만)
- run_line 을 엔진 choosing(고름)으로 감쌈 · 로직 자재 줄(차례 · 품목 · 단가 · 금액 · 가격 · 걸림) 내보냄
- 로직 자재 금액은 합 · 알약에 안 더함 · 사용자 자재와 같은 품목은 저장 때 400
- 단가승격 · 사전조합 안 로직도 고름 따름(없으면 지금 값)
- 프로젝트 사본 고른자재 씨앗 = 문서 고름 · 단가 묶음에 고른 품목 줄
- 화면: 자재목록 표 앞에 로직 자재 줄(삭제 없음 · [바꾸기] · 바꾼 줄 [기본]) · 입력 바뀌면 걸린 고름 풂 · 로직 자재와 같은 품목 추가 막음
- 시험 test_m02_labor_logic_mats.py(서버 · Node 짝)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016PSXsvyDxjNmufteN5bqmt
145 lines
5.7 KiB
TypeScript
145 lines
5.7 KiB
TypeScript
/* =============================================================================
|
|
* M02_MasterTemplete_Labor_LogicMats.ts
|
|
* 자재목록 표의 로직 자재 줄(103-1) — 담은 로직 맨 위 층 재료 줄(서버 calc `줄[].재료`) · 사용자 자재 줄과 같은 칸
|
|
* (코드 · 이름 · 규격 · 수량 · 단위 · 가격 다섯 · 단가 / 금액) · 사용자 자재 앞.
|
|
* · 삭제 없음 · [바꾸기] = M01 자재품목 표에서 고름(문서 `담은로직[].고름` · 엔진 `choosing`) · 바꾼 줄만 [기본](고름 풂)
|
|
* · 금액은 로직 단가 안이라 합에 다시 안 더함(보기만) · 수량은 로직 것(누름 · 알약 없음)
|
|
* · 하위 로직 안 재료는 안 보임(엔진이 맨 위 층만 고름)
|
|
* ========================================================================== */
|
|
|
|
import { createButton, el } from "@ui/ui_template_elements";
|
|
import { formatMoney } from "../M01_MasterData/M01_MasterData_UI_Logic_Money";
|
|
import { openPickTable } from "../M01_MasterData/M01_MasterData_UI_Logic_PickTable";
|
|
import { headWord } from "../M01_MasterData/M01_MasterData_UI_LogicLab_Modal";
|
|
import type { LaborCalc, LaborRow } from "./M02_MasterTemplete_Labor_Api";
|
|
import { lineId } from "./M02_MasterTemplete_Labor_Calc";
|
|
import { moneyCell, PRICE_SLOTS } from "./M02_MasterTemplete_Labor_Rows";
|
|
import { tc } from "./M02_MasterTemplete_Labor_Text";
|
|
import type { LogicMat } from "./M02_Labor_LogicMats_Model";
|
|
|
|
export interface LogicMatHandlers {
|
|
/** [바꾸기]로 고른 품목 키 — `row` = 담은 로직 차례 · `at` = 호표 차례 */
|
|
swap: (row: number, at: number, key: string) => void;
|
|
/** [기본] — 그 줄 고름 풂 */
|
|
reset: (row: number, at: number) => void;
|
|
}
|
|
|
|
const num = (v: number | null): string =>
|
|
v === null
|
|
? "—"
|
|
: Number(v.toPrecision(12)).toLocaleString("ko-KR", { maximumFractionDigits: 6 });
|
|
|
|
/** 담은 로직 차례대로 그 줄 로직 자재 — calc 답이 없으면 빈 것(답이 오면 다시 그림) */
|
|
export function logicMatRows(
|
|
rows: LaborRow[],
|
|
calc: LaborCalc | null,
|
|
wait: boolean,
|
|
on?: LogicMatHandlers,
|
|
): HTMLElement[] {
|
|
return rows.flatMap((row, r) => {
|
|
const line = row.참조 ? undefined : calc?.줄.find((l) => lineId(l) === lineId(row));
|
|
return (line?.재료 ?? []).map((mat) => matRow(row, r, mat, wait, on));
|
|
});
|
|
}
|
|
|
|
function matRow(
|
|
row: LaborRow,
|
|
r: number,
|
|
mat: LogicMat,
|
|
wait: boolean,
|
|
on?: LogicMatHandlers,
|
|
): HTMLElement {
|
|
const item = mat.품목;
|
|
const name = item?.이름 ?? mat.이름;
|
|
const owner = `${row.별칭 ?? row.로직} · ${mat.이름}`;
|
|
const priceCell = (slot: (typeof PRICE_SLOTS)[number]): HTMLElement => {
|
|
const base = !!item?.값칸 && item.값칸 === slot;
|
|
const v = mat.가격?.[slot];
|
|
return el("td", {
|
|
className: `m02-labrow__num m02-labrow__price${base ? " is-base-price" : ""}`,
|
|
text: wait ? "…" : v === null || v === undefined ? "—" : formatMoney(v),
|
|
attrs: { "data-label": slot, "data-price-slot": slot },
|
|
});
|
|
};
|
|
const money = moneyCell(mat.단가, mat.금액, wait, tc("Labor_AppliedSum"));
|
|
money.title = tc("LogicMat_Inside");
|
|
const tools = on
|
|
? [
|
|
el("td", {
|
|
className: "m02-labrow__move",
|
|
children: [
|
|
el("div", {
|
|
className: "ui-btn-group",
|
|
children: [
|
|
createButton({
|
|
label: tc("LogicMat_Swap"),
|
|
variant: "ghost",
|
|
onClick: (e) => {
|
|
e.stopPropagation();
|
|
openPickTable({
|
|
kind: "재료",
|
|
title: `${tc("LogicMat_PickTitle")} · ${mat.이름}`,
|
|
seed: headWord(name),
|
|
alt: [headWord(mat.이름)],
|
|
isCurrent: (x) => String(x["키"]) === item?.키,
|
|
onPick: (x) => on.swap(r, mat.차례, String(x["키"])),
|
|
});
|
|
},
|
|
}),
|
|
...(mat.고름
|
|
? [
|
|
createButton({
|
|
label: tc("LogicMat_Reset"),
|
|
variant: "ghost",
|
|
onClick: (e) => {
|
|
e.stopPropagation();
|
|
on.reset(r, mat.차례);
|
|
},
|
|
}),
|
|
]
|
|
: []),
|
|
],
|
|
}),
|
|
],
|
|
}),
|
|
]
|
|
: [];
|
|
return el("tr", {
|
|
className: `m02-labrow__row m02-labrow__logic-mat${mat.고름 ? " is-picked-mat" : ""}`,
|
|
attrs: { "data-labor-logic-mat": `${row.로직}|${mat.차례}` },
|
|
children: [
|
|
el("td", { className: "m02-labrow__code", text: item?.키 ?? "—" }),
|
|
el("td", {
|
|
className: "m02-labrow__name",
|
|
children: [
|
|
el("strong", { text: name, attrs: { title: name } }),
|
|
el("small", {
|
|
className: "m02-labrow__key",
|
|
text: mat.고름 ? `${owner} · ${tc("LogicMat_Picked")}` : owner,
|
|
attrs: { title: owner },
|
|
}),
|
|
...(mat.까닭 ? [el("small", { className: "m02-labrow__empty", text: mat.까닭 })] : []),
|
|
],
|
|
}),
|
|
el("td", {
|
|
className: "m02-labrow__spec",
|
|
text: item?.규격 ?? "",
|
|
attrs: { title: item?.규격 ?? "" },
|
|
}),
|
|
el("td", {
|
|
className: "m02-labrow__qty",
|
|
text: wait ? "…" : num(mat.수량),
|
|
attrs: { "data-label": tc("Labor_QtyCol") },
|
|
}),
|
|
el("td", {
|
|
className: "m02-labrow__unit",
|
|
text: mat.단위,
|
|
attrs: { "data-label": tc("Labor_Unit") },
|
|
}),
|
|
...PRICE_SLOTS.map(priceCell),
|
|
money,
|
|
...tools,
|
|
],
|
|
});
|
|
}
|