Files
eomsangdonandClaude Opus 5.5 598b80eb26 refactor(794-1b): M01 흐름도 패널 걷음 — 로직 상세 오른쪽 = 원문만 · [신규 로직] = 빈 시트 줄 · 흐름 서버 길 · DataFlow 화면 지움
요구 651 · 653 — 계산 공식 참고용 흐름도(① M01 로직 상세 오른쪽 접는 패널)를 걷음. ② M02 일위대가 다이어그램은 그대로.

지움(64 파일 · 18,666 줄 · 되돌릴 길 = git 이력)
- M01 6 파일 1,064 줄 — Store_Flow.py · UI_LogicLab_Flow.ts/.css · FlowRows.ts · FlowView.ts/.css
- M02_MasterTemplete/DataFlow 43 파일 12,799 줄 — 흐름도 화면 · 셈(Compute · Convert · Lookup · Model · Eval …)
- 시험 15 파일 4,803 줄 — test_m01_logic_flow{view,_engine,_project,_save} · test_m02_df_*(값 찾기 values_old 뺌) — 지운 흐름 코드만 지키던 것

남김(바닥) — DataFlow M02_DF_UnitPick(일위대가 셋) · M02_DF_Values(M02_Item_Prices) · M02_DF_Round · _standard 표 ·
  M02_DF_Router = GET /api/m02/df/standards 하나 · M02_DF_Api = cfStandards 하나 · CfRound 꼴은 Round 안으로

고침
- M01 Router — /save = Store_Make.save(개정 확인 · 자체 파일 새 줄 GX · added 그대로) · /calc /text /logic/auto = 옛 셈만 · /logic/flow 둘 지움
- master_formula — 흐름 훅 지움 · 줄에 「흐름」 이 남으면 「흐름도 로직은 걷음」 까닭 오류(저장된 흐름 로직 0 — 마스터 · storage 다시 확인)
- Sangeun_Calc._defaults = 옛 자동값 · M02_Logic_Lib_Read /logic/flow 지움 · main.py 주석
- 화면 — LogicLab(startFlow · scheduleFlow · 예시값 잇기 지움 · [신규 로직] 줄 = 입력 · 중간 · 호표 빈 목록 + 끝수 null) ·
  Side(원문 패널 하나) · Source(머리 칸 이름 m01lab-src__top/__title · DF_Panel.css 안 씀) · Detail · View(nowInputs 지움) · Sheet(흐름 갈래 지움) · 글
- 시험 — 지운 파일을 가리키던 목록 넷(spreadsheet_size · 208_1 · 588 · input_prefs) · 717_1 흐름 길 두 줄 · values_old 흐름 셈 부분 지움
- test_713_3_list 기대 — 로직 1,461 → 1,515 · 묶음 307 → 335 · 작업량 21 → 65 · 부르는 506 → 512(782-1 · 2 · 3 건설 기초 로직 54 더함 ·
  옛 키만 세면 307 · 21 · 506 그대로)
- 새 지킴 test_794_1b_flow_gone — 흐름 길 404 · 표준 표 남음 · 신규 로직 자체 GX · 흐름 남은 줄 까닭 오류

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 19:38:39 +09:00

126 lines
4.9 KiB
TypeScript

/* =============================================================================
* M02_DF_Round.ts
* 끝수 기본값 — 금액 단위 표준(산림품셈 1-2-2 「금액의 단위표준」)을 읽어
* 출력(비용) 기본 = 일위대가표 금액란 · 원가 합계 기본 = 일위대가표 계금 (모두 미만 버림)
* 표 읽기 = 서버 `GET /api/m02/df/standards`(Api `cfStandards`) → {unit, money, saved}
* 모양은 docs/raw/M01_쉬운단가/흐름도_정함.md 11 · 12.
* ========================================================================== */
import { cfStandards } from "./M02_DF_Api";
/** 끝수 — 자리 · 방법 */
export interface CfRound {
places: number;
method: "반올림" | "올림" | "버림";
}
/** 표 한 줄 — 원문 그대로(종목 · 단위 · 지위 · 비고) */
export interface CfMoneyRow {
종목: string;
단위?: string;
지위: number;
비고?: string;
}
/** 표 모양 둘(`{줄: […]}` · 줄 목록)을 줄 목록으로 */
const rowsOf = (x: unknown): CfMoneyRow[] => {
const list = Array.isArray(x) ? x : (x as { 줄?: unknown } | null)?.줄;
return Array.isArray(list) ? (list as CfMoneyRow[]) : [];
};
let loaded: Promise<CfMoneyRow[]> | null = null;
/** 금액 단위 표준 줄 — 서버가 고친 것(`_data`)이 있으면 그것 · 없으면 처음 값을 줌 */
export function loadMoneyStandard(): Promise<CfMoneyRow[]> {
loaded ??= cfStandards().then(
(s) => rowsOf(s.money),
() => {
loaded = null; // 못 읽었으면 다음에 다시 — 지금은 정해 둔 기본값으로
return [];
},
);
return loaded;
}
/** 표를 다시 읽게(표를 고친 뒤) */
export function resetStandard(): void {
loaded = null;
}
/** 표 줄 → 끝수 — 지위 0.1 = 소수 1자리 · 1 이상 = 0자리 · 비고 「미만버림」 */
export function roundOf(row: CfMoneyRow): CfRound {
const step = row.지위 > 0 ? row.지위 : 1;
const method =
/올림/.test(row.비고 ?? "") && !/버림/.test(row.비고 ?? "")
? "올림"
: /반올림/.test(row.비고 ?? "")
? "반올림"
: "버림";
return { places: step >= 1 ? 0 : Math.round(-Math.log10(step)), method };
}
const find = (rows: CfMoneyRow[], word: string): CfMoneyRow | undefined =>
rows.find((r) => r.종목.includes("일위대가표") && r.종목.includes(word));
/** 출력(비용) 기본 — 일위대가표 금액란 0.1원 미만 버림 */
export async function defaultItemRound(): Promise<CfRound> {
const row = find(await loadMoneyStandard(), "금액란");
return row ? roundOf(row) : { places: 1, method: "버림" };
}
/** 원가 합계 기본 — 일위대가표 계금 1원 미만 버림 */
export async function defaultTotalRound(): Promise<CfRound> {
const row = find(await loadMoneyStandard(), "계금");
return row ? roundOf(row) : { places: 0, method: "버림" };
}
/** 끝수 적용 — 소수 자리 · 방법(부동소수 오차는 아주 작은 값으로 눌러 줌) */
export function roundBy(r: CfRound | null | undefined, v: number): number {
if (!r) return v;
const k = 10 ** r.places;
const x = v * k;
const eps = Math.abs(x) * 1e-12 + 1e-9;
const f =
r.method === "올림"
? Math.ceil(x - eps)
: r.method === "반올림"
? Math.round(x + eps)
: Math.floor(x + eps);
return f / k;
}
/** 「0.1원 미만 버림」 꼴 쉬운 말 */
export function roundText(r: CfRound | null): string {
if (!r) return "끝수 없음(값 그대로)";
const step = r.places > 0 ? Number((10 ** -r.places).toFixed(r.places)) : 1;
return `${step}원 미만 ${r.method}`;
}
/** 출력 기본 끝수(표를 읽어 오기 전에는 금액란 0.1원 미만 버림) — 화면이 바로 쓰는 값 */
let itemNow: CfRound = { places: 1, method: "버림" };
void defaultItemRound().then((r) => (itemNow = r));
export const itemRoundNow = (): CfRound => itemNow;
/** 끝수대로 자른 값을 그 자리 수만큼 보임(3,216.6) — 끝수가 없으면 소수 여섯째 자리까지 */
export function showRounded(v: number, r?: CfRound): string {
if (!r) return v.toLocaleString("ko-KR", { maximumFractionDigits: 6 });
return roundBy(r, v).toLocaleString("ko-KR", {
minimumFractionDigits: 0,
maximumFractionDigits: Math.max(0, r.places),
});
}
/** 원가 합계 기본 끝수(표를 읽어 오기 전에는 계금 1원 미만 버림) — 화면이 바로 쓰는 값 */
let totalNow: CfRound = { places: 0, method: "버림" };
void defaultTotalRound().then((r) => (totalNow = r));
export const totalRoundNow = (): CfRound => totalNow;
/** 그 로직의 합계 끝수 — 정본 = CfLogic.totalRound(옛 로직을 옮긴 흐름 = 자신의 끝수 · null = 끝수 없음) ·
* 정하지 않았으면 fallback(새 로직 = 일위대가표 계금 1원 미만 버림) */
export function totalRoundOf(
lg: { totalRound?: CfRound | null },
fallback: CfRound = totalNow,
): CfRound | null {
return lg.totalRound === undefined ? fallback : lg.totalRound;
}