From 6008a1e7aeb4cae60385cffcd6dcb97cf359c3be Mon Sep 17 00:00:00 2001 From: umsangdon Date: Thu, 1 Oct 2026 19:39:48 +0900 Subject: [PATCH] =?UTF-8?q?feat(m02-df):=20=ED=9D=90=EB=A6=84=EB=8F=84=20M?= =?UTF-8?q?odel=20=EB=84=93=ED=9E=8C=20=ED=83=80=EC=9E=85=20=C2=B7=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EB=A8=B8=EB=A6=AC=20=EC=98=AC=EB=A6=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 재료 요소 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 Claude-Session: https://claude.ai/code/session_01VBToTCGk5dqDKeXCEP3vhk --- M02_MasterTemplete/DataFlow/M02_DF_Eval.ts | 22 ++++ M02_MasterTemplete/DataFlow/M02_DF_Model.ts | 106 +++++++++++++++++++- M02_MasterTemplete/DataFlow/M02_DF_Rule.ts | 33 ++++++ 3 files changed, 156 insertions(+), 5 deletions(-) create mode 100644 M02_MasterTemplete/DataFlow/M02_DF_Rule.ts diff --git a/M02_MasterTemplete/DataFlow/M02_DF_Eval.ts b/M02_MasterTemplete/DataFlow/M02_DF_Eval.ts index b955c2d61..f471e0145 100644 --- a/M02_MasterTemplete/DataFlow/M02_DF_Eval.ts +++ b/M02_MasterTemplete/DataFlow/M02_DF_Eval.ts @@ -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("준비 중"); +} diff --git a/M02_MasterTemplete/DataFlow/M02_DF_Model.ts b/M02_MasterTemplete/DataFlow/M02_DF_Model.ts index 3613bf589..6191c361d 100644 --- a/M02_MasterTemplete/DataFlow/M02_DF_Model.ts +++ b/M02_MasterTemplete/DataFlow/M02_DF_Model.ts @@ -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(); +} diff --git a/M02_MasterTemplete/DataFlow/M02_DF_Rule.ts b/M02_MasterTemplete/DataFlow/M02_DF_Rule.ts new file mode 100644 index 000000000..e8f410de8 --- /dev/null +++ b/M02_MasterTemplete/DataFlow/M02_DF_Rule.ts @@ -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 = {}, +): CfRuleResult { + throw new Error("준비 중"); +} + +/** 끝수 적용 — 엔진 ROUND · ROUNDUP · ROUNDDOWN */ +export function applyRound(_round: CfRound, _value: number): number { + throw new Error("준비 중"); +}