feat(m02-df): 흐름도 Model 넓힌 타입 · 함수 머리 올림

- 재료 요소 mode(fixed · choice · rule) · scope · rule{field · op · from} · default · slot
- 입력 갈래 logic + CfLogicRef{key · name · outputs · designs}
- 선 규칙 CfLink.rule — lookup · gate · scale · round · CfRound(func.output.round · output.round)
- 흐름 CfStep · CfDesign · traceBack · traceForward · designInputs · Eval CfBreak · breaks
- M02_DF_Rule.ts — ruleLabel · applyRule · applyRound (속은 준비 중)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VBToTCGk5dqDKeXCEP3vhk
This commit is contained in:
2026-10-01 19:39:48 +09:00
co-authored by Claude Opus 5.5
parent fc7affb019
commit 6008a1e7ae
3 changed files with 156 additions and 5 deletions
@@ -208,3 +208,25 @@ export function checkLogic(lg: CfLogic, values: CfValues = {}): string[] {
out.push(tl("NoValue", { box: b.name, pill }));
return [...out, ...ev.errors];
}
/** 끊긴 곳 하나 — 상자 · 알약 · 선 위에 표시할 것 */
export interface CfBreak {
kind:
| "빈 받는 자리"
| "통과 안 됨"
| "표에 없는 조건"
| "맞는 재료 없음"
| "조건 겹침"
| "조건 안 맞음"
| "오류";
box: string;
port?: string;
link?: string;
/** 쉬운 말 */
text: string;
}
/** 끊긴 곳 목록(넓힘 · 준비 중) */
export function breaks(_lg: CfLogic, _values: CfValues = {}): CfBreak[] {
throw new Error("준비 중");
}
+101 -5
View File
@@ -1,7 +1,8 @@
/* =============================================================================
* M02_DF_Model.ts
* 단가산출 다이어그램 Model — 입력 · 함수 · 출력 상자를 선으로 이음 · 함수 상자 = 시트 하나(출력 하나)
* DOM 없음 · 모양은 `docs/raw/M01_쉬운단가/원가다이어그램_정함.md` 「상자 · 선 약속」 그대로
* 단가산출 흐름도 Model — 입력 · 함수 · 출력 상자를 선으로 이음 · 함수 상자 = 시트 하나(출력 하나)
* 선 규칙(표에서 찾기 · 조건 통과 · 바꾸기 · 끝수) · 재료 방식 셋 · 다른 로직 입력 · 흐름 따라가기 · 설계 때 정할 것
* DOM 없음 · 모양은 `docs/raw/M01_쉬운단가/흐름도_정함.md` 4~8 · 12 · 1판 약속 `원가다이어그램_정함.md`
* ========================================================================== */
import { currentLanguageIndex } from "@ui/ui_template_locale";
@@ -11,12 +12,36 @@ export type CfKind = "input" | "function" | "output";
export type CfGroup = "인력" | "재료" | "유가전력" | "기계" | "소요량" | "계수" | "환율" | "요율";
export type CfCost = "노무비" | "재료비" | "경비" | "값";
/** 입력 상자가 고른 요소 — ref = 마스터 키 또는 사용자 요소 id */
/** 재료 요소 방식 — 고정(품목 하나) · 선택 자리(프로젝트에서 고름) · 조건 찾기(규격 / 이름에 알약 값) */
export type CfMaterialMode = "fixed" | "choice" | "rule";
/** 재료 찾을 범위 */
export interface CfScope {
구분?: string;
상세구분?: string;
찾는말?: string;
}
/** 재료 조건 찾기 — 품목의 규격 / 이름에 [알약 값] 이 포함 · 같음 */
export interface CfMaterialRule {
field: "규격" | "이름";
op: "포함" | "같음";
from: CfPort;
}
/** 입력 상자가 고른 요소 — ref = 마스터 키 또는 사용자 요소 id
* 재료면 mode · scope · rule · default · slot(알약 이름 = 자리 이름 · 없으면 name) */
export interface CfElementRef {
ref: string;
name: string;
spec: string;
unit: string;
mode?: CfMaterialMode;
scope?: CfScope;
rule?: CfMaterialRule;
/** 기본 품목 키(선택 자리 · 조건 찾기) */
default?: string;
slot?: string;
}
/** 변수(입력 정의) — 그룹 밖 정보(면적 · 길이 …) · 함수 상자의 받는 값 */
@@ -31,13 +56,31 @@ export interface CfVariable {
cell?: string;
}
/** 다른 로직 입력 — 저장 로직(DF-…) · 견본 id · outputs · designs 는 고를 때 그 로직에서 읽어 둔 것 */
export interface CfLogicRef {
key: string;
name?: string;
/** 내보내는 알약 = 그 로직 출력 상자 이름(비용 · 값) */
outputs: CfPill[];
/** 받는 알약 = 그 로직의 「설계 때 정할 것」(변수 · 재료 자리) */
designs: CfPill[];
}
export interface CfInput {
mode: "group" | "variable";
mode: "group" | "variable" | "logic";
group?: CfGroup;
elements: CfElementRef[];
/** 소요량 · 계수 = 표(row 가 있으면 그 한 줄) */
table?: { key: string; row?: number };
variables: CfVariable[];
/** mode "logic" */
logic?: CfLogicRef;
}
/** 끝수 — 소수 자리 · 방법 */
export interface CfRound {
places: number;
method: "반올림" | "올림" | "버림";
}
export interface CfFunc {
@@ -47,12 +90,13 @@ export interface CfFunc {
inputs: { port: string; cell: string }[];
variables: CfVariable[];
/** 내보내는 알약 하나 */
output: { cell: string; name: string; unit: string };
output: { cell: string; name: string; unit: string; round?: CfRound };
}
export interface CfOutput {
cost: CfCost;
unit: string;
round?: CfRound;
}
export interface CfBox {
@@ -75,10 +119,41 @@ export interface CfPort {
name: string;
}
export type CfGateOp = "=" | "≠" | "≥" | "≤" | ">" | "<";
/** 선 규칙(선 하나에 하나 · 없으면 그대로) — 받는 칸의 엑셀 수식으로 바뀌어 엔진 한 벌로 셈
* lookup = 보낸 표에서 조건 칸마다 맞출 알약 · 꺼낼 칸(범위 칸은 「이름·아래」 「이름·위」)
* gate = 알약 값이 조건을 통과할 때만 보냄 · scale = × ÷ +% -% · round = 끝수 */
export type CfRule =
| { kind: "lookup"; conds: { col: string; from: CfPort }[]; col: string }
| { kind: "gate"; from: CfPort; op: CfGateOp; value: number | string }
| { kind: "scale"; op: "×" | "÷" | "+%" | "-%"; value: number }
| ({ kind: "round" } & CfRound);
export interface CfLink {
id: string;
from: CfPort;
to: CfPort;
rule?: CfRule;
}
/** 흐름 한 걸음 — 그 알약 · 그 알약으로 들어온(나간) 선의 규칙 */
export interface CfStep {
box: string;
port: string;
dir: "in" | "out";
link?: string;
rule?: CfRule;
}
/** 설계 때 정할 것 — 변수 · 재료 자리 · 다른 로직의 받는 알약(선이 없을 때) */
export interface CfDesign {
box: string;
port: string;
kind: "변수" | "재료 자리" | "다른 로직";
unit: string;
variable?: CfVariable;
element?: CfElementRef;
}
/** 사용자 요소 — 그룹에 없는 새 요소(마스터에 안 씀 · `_data` 에 저장) */
@@ -327,3 +402,24 @@ export function unlink(lg: CfLogic, id: string): CfLogic {
const inputs = box.func.inputs.filter((i) => i !== port);
return updateBox(next, box.id, { func: { ...box.func, inputs } });
}
/* ── 흐름(넓힘 · 준비 중) ───────────────────────────────────────────────── */
const notReady = (): never => {
throw new Error("준비 중");
};
/** 그 알약 값이 온 길 — 출처 사슬(가까운 것부터) */
export function traceBack(_lg: CfLogic, _port: CfPort): CfStep[] {
return notReady();
}
/** 그 알약 값이 가는 길 */
export function traceForward(_lg: CfLogic, _port: CfPort): CfStep[] {
return notReady();
}
/** 설계 때 정할 것 — 변수 · 재료 자리(선택) · 다른 로직의 받는 알약(선이 없을 때) */
export function designInputs(_lg: CfLogic): CfDesign[] {
return notReady();
}
@@ -0,0 +1,33 @@
/* =============================================================================
* M02_DF_Rule.ts
* 선 규칙 · 끝수 → 엑셀 수식 — 우리 스프레드시트 엔진 한 벌로 셈 · 선 위 규칙 글(쉬운 말) · DOM 없음
* ========================================================================== */
import type { CfRound, CfRule } from "./M02_DF_Model";
/** 규칙을 지난 값 — 통과 안 됨 · 표에 없는 조건 · 둘 이상 맞음은 value null + why */
export interface CfRuleResult {
value: number | string | boolean | null;
why: "통과 안 됨" | "표에 없는 조건" | "표에 여럿 맞음" | "오류" | null;
/** 받는 칸에 들어갈 엑셀 수식(보이기 · 시험) */
formula: string;
}
/** 선 위에 늘 보일 규칙 글 */
export function ruleLabel(_rule: CfRule): string {
throw new Error("준비 중");
}
/** 규칙 적용 — value = 보낸 알약 값(표면 머리 줄 포함 칸 덩어리) · conds = 조건 알약 값(lookup · gate) */
export function applyRule(
_rule: CfRule,
_value: unknown,
_conds: Record<string, unknown> = {},
): CfRuleResult {
throw new Error("준비 중");
}
/** 끝수 적용 — 엔진 ROUND · ROUNDUP · ROUNDDOWN */
export function applyRound(_round: CfRound, _value: number): number {
throw new Error("준비 중");
}