Files
Aislo/M01_MasterData/M01_MasterData_UI_LogicLab_FlowRows.ts
T
eomsangdonandClaude Opus 5.5 d2ab8ee8cc fix(m02-df): 변환 흐름 화면 흠 넷 — 변수 예시값 · 표 찾기 실패 같은 뜻 · 다른 로직 서버 값 · 합계 끝수
- 변환: 설계 값 변수 예시값 = 줄 시험입력 · 없으면 check_master.시험입력 기본(글 수 → 수) · totalRound = null(옛 계는 끝수 없음)
- Model CfLogic.totalRound(없음 = 기본 계금 1원 미만 버림 · null = 끝수 없음) · Round totalRoundFor · roundBy/roundText 가 null 받음 · Summary · M01 흐름 보기 합계가 그것을 씀
- Compute: 함수 결과 오류는 오류 값(「#N/A」 등)으로 넘김(화면 Eval 과 같음 · 받는 칸으로 번짐 · 옛 엔진은 그 줄을 비움) — 서버가 빈 칸 0 으로 계속 세던 차이 없앰
- 화면 값 물음(Entry_Detail queriesOf): 인력 · 기계 · 유가 조건 찾기도 자리 물음 · 재료 검색어 틀은 채운 글 · 다른 로직 입력은 서버 /logic-value 로 받은 설계 값째 셈(그 안 조건 찾기 · 단가까지) · Eval 은 서버 출력이 있으면 그것
- Values.one_value: 인력 · 기계 · 유가 자리 물음 = group_find
- 전수 대조 시험입력 1,414/1,472(96.1%) · 기본 입력 1,429(97.1%) — 남은 49 는 sub4 범위규칙 칸 · 검색어 찾기 · 재료 값 차이

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VBToTCGk5dqDKeXCEP3vhk
2026-10-02 00:56:28 +09:00

123 lines
4.0 KiB
TypeScript

/* =============================================================================
* M01_MasterData_UI_LogicLab_FlowRows.ts
* 흐름 로직의 왼쪽 「단가산출 상세」 줄 만들기(DOM 없음 · 순수) — 흐름도 출력 상자를 비용 종류별로 묶어
* 줄(이름 · 단위 · 값) · 소계 · 계 · 함수 상자별 결과 값. 값은 DataFlow 계산 결과(`CfEvalResult`)를 읽기만 함.
* 모양은 docs/raw/M01_쉬운단가/흐름도_적용_정함.md 7. 시험 = resources/tester/test_m01_logic_flowview.py
* ========================================================================== */
import type { CfEvalResult } from "../M02_MasterTemplete/DataFlow/M02_DF_Eval";
import type { CfBox, CfCost, CfLogic, CfRound } from "../M02_MasterTemplete/DataFlow/M02_DF_Model";
import { roundBy, showRounded } from "../M02_MasterTemplete/DataFlow/M02_DF_Round";
export type FlowGroupKey = "labor" | "material" | "cost" | "plain";
/** 비용 종류 → 묶음(인력 = 노무비 · 재료 = 재료비 · 경비) · 값(비용 아님)은 따로 */
export const GROUP_OF: Record<CfCost, FlowGroupKey> = {
노무비: "labor",
재료비: "material",
경비: "cost",
값: "plain",
};
export const GROUP_ORDER: FlowGroupKey[] = ["labor", "material", "cost", "plain"];
const COST_OF: Record<Exclude<FlowGroupKey, "plain">, "노무비" | "재료비" | "경비"> = {
labor: "노무비",
material: "재료비",
cost: "경비",
};
export interface FlowLine {
box: string;
name: string;
unit: string;
value: number | string | boolean | null;
/** 보이는 글 — 숫자는 그 출력의 끝수 자리대로 */
text: string;
}
export interface FlowGroup {
key: FlowGroupKey;
lines: FlowLine[];
/** 비용 묶음만 — 합계 끝수를 적용한 소계 */
sub: number | null;
subText: string;
}
export interface FlowFunc {
box: string;
name: string;
unit: string;
value: unknown;
text: string;
}
export interface FlowRows {
groups: FlowGroup[];
/** 합계 끝수(기본 1원 미만 버림)를 적용한 계 */
total: number;
totalText: string;
funcs: FlowFunc[];
errors: string[];
}
const plain = (v: unknown): string =>
v === null || v === undefined || Array.isArray(v)
? "—"
: typeof v === "number"
? v.toLocaleString("ko-KR", { maximumFractionDigits: 6 })
: String(v);
/** 줄 모으기 — round.item = 출력 기본 끝수 · round.total = 합계 기본 끝수 */
export function flowRows(
lg: CfLogic,
ev: CfEvalResult,
round: { item: CfRound; total: CfRound | null },
): FlowRows {
const outs: CfBox[] = lg.boxes.filter((b) => b.kind === "output");
const groups = GROUP_ORDER.map((key): FlowGroup => {
const mine = outs.filter((b) => GROUP_OF[b.output?.cost ?? "값"] === key);
const lines = mine.map((b): FlowLine => {
const v = ev.outputs[b.id] ?? null;
const r = b.output?.round ?? (key === "plain" ? undefined : round.item);
return {
box: b.id,
name: b.name,
unit: b.output?.unit ?? b.unit,
value: v,
text: typeof v === "number" ? showRounded(v, r) : plain(v),
};
});
const raw = key === "plain" ? null : ev.cost[COST_OF[key]];
const sub = raw === null ? null : roundBy(round.total, raw);
return {
key,
lines,
sub,
subText: sub === null ? "" : showRounded(sub, round.total ?? undefined),
};
}).filter((g) => g.lines.length);
const total = roundBy(round.total, ev.cost.계);
const funcs = lg.boxes
.filter((b) => b.kind === "function" && b.func)
.map((b): FlowFunc => {
const out = b.func!.output;
const v = ev.ports[b.id]?.[out.name];
return {
box: b.id,
name: b.name,
unit: out.unit || b.unit,
value: v,
text:
typeof v === "number"
? v.toLocaleString("ko-KR", { maximumFractionDigits: 6 })
: plain(v),
};
});
return {
groups,
total,
totalText: showRounded(total, round.total ?? undefined),
funcs,
errors: ev.errors,
};
}