fix(m01): 흐름 합계 끝수 = 비용 출력 상자에 실린 로직 자신의 끝수 · 새 로직만 기본 1원 미만 버림
왼쪽 흐름 보기 소계 · 계와 오른쪽 원가 합계가 같은 끝수를 씀(Round.totalRoundOf) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EauA2ATmqNRbeV48s9CcBr
This commit is contained in:
@@ -21,7 +21,12 @@ import {
|
||||
type FormulaLine,
|
||||
} from "../M02_MasterTemplete/DataFlow/M02_DF_FormulaView";
|
||||
import type { CfLogic } from "../M02_MasterTemplete/DataFlow/M02_DF_Model";
|
||||
import { itemRoundNow, totalRoundNow } from "../M02_MasterTemplete/DataFlow/M02_DF_Round";
|
||||
import {
|
||||
itemRoundNow,
|
||||
roundText,
|
||||
totalRoundNow,
|
||||
totalRoundOf,
|
||||
} from "../M02_MasterTemplete/DataFlow/M02_DF_Round";
|
||||
import {
|
||||
flowRows,
|
||||
type FlowFunc,
|
||||
@@ -42,7 +47,7 @@ const TEXT = {
|
||||
HValue: ["값", "Value"],
|
||||
Sub: ["소계", "Subtotal"],
|
||||
Total: ["계", "Total"],
|
||||
TotalNote: ["합계 끝수 기본 = 1원 미만 버림", "Total rounding default: floor to 1 won"],
|
||||
TotalNote: ["합계 끝수", "Total rounding"],
|
||||
None: ["출력 상자가 없음", "No output box"],
|
||||
NoFunc: ["함수 상자가 없음", "No function box"],
|
||||
} as const;
|
||||
@@ -158,7 +163,8 @@ export function buildFlowView(
|
||||
);
|
||||
return;
|
||||
}
|
||||
const rows = flowRows(lg, ev, { item: itemRoundNow(), total: totalRoundNow() });
|
||||
const totalRound = totalRoundOf(lg, totalRoundNow());
|
||||
const rows = flowRows(lg, ev, { item: itemRoundNow(), total: totalRound });
|
||||
const detail = section(tf("Detail"), [
|
||||
...(rows.groups.length
|
||||
? rows.groups.map(groupTable)
|
||||
@@ -168,7 +174,10 @@ export function buildFlowView(
|
||||
attrs: { "data-sum": String(rows.total) },
|
||||
text: `${tf("Total")} ${rows.totalText}`,
|
||||
}),
|
||||
el("p", { className: "m01-logic__muted", text: tf("TotalNote") }),
|
||||
el("p", {
|
||||
className: "m01-logic__muted",
|
||||
text: `${tf("TotalNote")}: ${roundText(totalRound)}`,
|
||||
}),
|
||||
...rows.errors.slice(0, 3).map((t) => el("p", { className: "m01flow__warn", text: t })),
|
||||
]);
|
||||
/** 함수 상자마다 식 두 줄(sub2 식 보기 · 받는 알약 값을 칸에 넣고 셈) — 못 그리면 이름 = 값만 */
|
||||
|
||||
@@ -107,3 +107,15 @@ export function showRounded(v: number, r?: CfRound): string {
|
||||
let totalNow: CfRound = { places: 0, method: "버림" };
|
||||
void defaultTotalRound().then((r) => (totalNow = r));
|
||||
export const totalRoundNow = (): CfRound => totalNow;
|
||||
|
||||
/** 그 로직의 합계 끝수 — 비용 출력 상자에 끝수가 실려 있으면 그것(옛 로직을 옮긴 흐름은 자신의 끝수) ·
|
||||
* 없으면 기본(새 로직 = 일위대가표 계금 1원 미만 버림) */
|
||||
export function totalRoundOf(
|
||||
lg: { boxes: { kind: string; output?: { cost: string; round?: CfRound } }[] },
|
||||
fallback: CfRound,
|
||||
): CfRound {
|
||||
const hit = lg.boxes.find(
|
||||
(b) => b.kind === "output" && b.output?.cost !== "값" && b.output?.round,
|
||||
);
|
||||
return hit?.output?.round ?? fallback;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,14 @@ import { formatMoney } from "../../M01_MasterData/M01_MasterData_UI_Logic_Money"
|
||||
import type { CfEvalOptions, CfEvalResult, CfValues } from "./M02_DF_Eval";
|
||||
import type { CfBox, CfLogic, CfRound } from "./M02_DF_Model";
|
||||
import { showValue } from "./M02_DF_OutputPanel";
|
||||
import { defaultTotalRound, itemRoundNow, roundBy, roundText, showRounded } from "./M02_DF_Round";
|
||||
import {
|
||||
defaultTotalRound,
|
||||
itemRoundNow,
|
||||
roundBy,
|
||||
roundText,
|
||||
showRounded,
|
||||
totalRoundOf,
|
||||
} from "./M02_DF_Round";
|
||||
import { renderVerify, type CfSel } from "./M02_DF_Verify";
|
||||
import "./M02_DF_Summary.css";
|
||||
|
||||
@@ -69,18 +76,20 @@ export function renderSummary(
|
||||
? ts("Total")
|
||||
: ts("Per", { per: /^\d/.test(perUnit) ? perUnit : `1${perUnit}` });
|
||||
const { cost } = result;
|
||||
/** 합계 끝수 — 비용 출력 상자에 실린 끝수(옮긴 로직의 자신 끝수) · 없으면 기본 1원 미만 버림 */
|
||||
const sumRound = totalRoundOf(lg, totalRound);
|
||||
const big = el("p", {
|
||||
className: "m02-dfsum__big",
|
||||
children: [
|
||||
el("span", { text: money ? perText : ts("Total") }),
|
||||
el("strong", { text: `${formatMoney(roundBy(totalRound, cost.계))} ${ts("Won")}` }),
|
||||
el("strong", { text: `${formatMoney(roundBy(sumRound, cost.계))} ${ts("Won")}` }),
|
||||
],
|
||||
});
|
||||
const costs = el("dl", {
|
||||
className: "m02-dfsum__costs",
|
||||
children: COSTS.flatMap((c) => [
|
||||
el("dt", { text: ts(COST_KEY[c]) }),
|
||||
el("dd", { text: formatMoney(roundBy(totalRound, cost[c])) }),
|
||||
el("dd", { text: formatMoney(roundBy(sumRound, cost[c])) }),
|
||||
]),
|
||||
});
|
||||
|
||||
@@ -161,7 +170,7 @@ export function renderSummary(
|
||||
: []),
|
||||
el("p", {
|
||||
className: "m02-dfsum__muted",
|
||||
text: `${ts("RoundTotal")}: ${roundText(totalRound)}`,
|
||||
text: `${ts("RoundTotal")}: ${roundText(sumRound)}`,
|
||||
}),
|
||||
verifyHost,
|
||||
);
|
||||
|
||||
@@ -37,8 +37,9 @@ function load(f) {
|
||||
return box.exports;
|
||||
}
|
||||
const m = load(file);
|
||||
const r = load(path.join(root, "M02_MasterTemplete", "DataFlow", "M02_DF_Round.ts"));
|
||||
const jobs = JSON.parse(fs.readFileSync(0, "utf8"));
|
||||
const out = jobs.map((job) => m.flowRows(job.lg, job.ev, job.round));
|
||||
const out = jobs.map((job) => job.op === "totalRound" ? r.totalRoundOf(job.lg, job.fallback) : m.flowRows(job.lg, job.ev, job.round));
|
||||
process.stdout.write(JSON.stringify(out));
|
||||
setTimeout(() => process.exit(0), 50);
|
||||
"""
|
||||
@@ -182,3 +183,33 @@ def test_missing_value_shows_dash():
|
||||
rows = run([{"lg": lg, "ev": ev, "round": ROUND}])[0]
|
||||
assert group(rows, "labor")["lines"][0]["text"] == "—"
|
||||
assert rows["errors"] == ["끊김"]
|
||||
|
||||
|
||||
def test_total_round_follows_converted_logic_own_round():
|
||||
"""옮긴 로직 = 비용 출력 상자에 실린 끝수 · 새 로직(끝수 없음) = 기본."""
|
||||
default = {"places": 0, "method": "버림"}
|
||||
own = {"places": 1, "method": "반올림"}
|
||||
converted = logic(
|
||||
[out_box("o1", "인건비", "노무비", round_=own), out_box("o2", "작업량", "값")]
|
||||
)
|
||||
fresh = logic([out_box("o1", "인건비", "노무비")])
|
||||
only_plain = logic([out_box("o3", "작업량", "값", round_={"places": 3, "method": "버림"})])
|
||||
got = run(
|
||||
[
|
||||
{"op": "totalRound", "lg": converted, "fallback": default},
|
||||
{"op": "totalRound", "lg": fresh, "fallback": default},
|
||||
{"op": "totalRound", "lg": only_plain, "fallback": default},
|
||||
]
|
||||
)
|
||||
assert got == [own, default, default] # 값(비용 아님)의 끝수는 합계 끝수가 아님
|
||||
|
||||
|
||||
def test_subtotal_and_total_use_converted_round():
|
||||
own = {"places": 1, "method": "반올림"}
|
||||
lg = logic([out_box("o1", "인건비", "노무비", round_=own)])
|
||||
ev = result(
|
||||
{"o1": 3216.7}, {"노무비": 3216.7, "재료비": 0, "경비": 0, "값들": {}, "계": 3216.7}
|
||||
)
|
||||
rows = run([{"lg": lg, "ev": ev, "round": {"item": ROUND["item"], "total": own}}])[0]
|
||||
assert group(rows, "labor")["subText"] == "3,216.7"
|
||||
assert rows["totalText"] == "3,216.7"
|
||||
|
||||
Reference in New Issue
Block a user