Merge remote-tracking branch 'origin/sub_laptop_3' into main_laptop_1
This commit is contained in:
@@ -193,3 +193,23 @@ def test_mirror_random_docs_match_decimal():
|
||||
assert out["계산"] == {r: {n: _plain(v) for n, v in w.items()} for r, w in want.items()}
|
||||
for name, total in sums.items():
|
||||
assert out["합계"]["s"][name] == _plain(total)
|
||||
|
||||
|
||||
def test_text_in_number_column_is_flagged_and_skipped():
|
||||
doc = _doc(
|
||||
[{"id": "a"}, {"id": "n", "꼴": "글"}, {"id": "f", "식": "[a]*2"}],
|
||||
[
|
||||
{"id": "r1", "값": {"a": 5, "n": "abc"}},
|
||||
{"id": "r2", "값": {"a": "abc", "n": "x"}},
|
||||
{"id": "r3", "값": {"a": "1,000"}},
|
||||
{"id": "r4", "값": {"a": ""}},
|
||||
],
|
||||
합계줄=[{"id": "t", "이름": "합계", "식": "SUM"}],
|
||||
)
|
||||
out = _run(doc)
|
||||
assert out["합계"]["t"]["a"] == "1005" # abc 는 빼고 · 1,000 은 수 · 빈 칸은 0
|
||||
errors = {(e["줄"], e["열"]): e["까닭"] for e in out["오류"]}
|
||||
assert errors[("r2", "a")] == "수가 아님"
|
||||
assert errors[("t", "a")].startswith("수가 아닌 칸 1 개")
|
||||
# 글 칸 · 정상 수 칸은 표시 없음(r2 의 식 칸은 글을 곱해 식 오류 — 따로)
|
||||
assert not any(k[1] == "n" or k[0] in ("r1", "r3", "r4") for k in errors)
|
||||
|
||||
@@ -34,6 +34,13 @@ import type {
|
||||
|
||||
const ROUND_MODE = { 반올림: "round", 올림: "away", 버림: "trunc" } as const;
|
||||
|
||||
/** 수 칸에 수가 아닌 글 — 계산은 그 칸을 빼고 하되 알림(엑셀처럼 무시 · 오류 목록에는 이 까닭으로). */
|
||||
export const NOT_A_NUMBER = "수가 아님";
|
||||
export const SKIPPED_PREFIX = "수가 아닌 칸";
|
||||
/** 값은 그대로 두고 알리기만 하는 까닭인가 — 화면이 글을 「#오류」 로 바꾸지 않게. */
|
||||
export const isNotice = (why: string): boolean =>
|
||||
why === NOT_A_NUMBER || why.startsWith(SKIPPED_PREFIX);
|
||||
|
||||
export const isNumberColumn = (col: SheetColumn): boolean => col.꼴 !== "글";
|
||||
|
||||
/** 합계 줄 한 칸의 식 — 없으면 null(빈 칸). `"SUM"` 은 그 열의 열 합. */
|
||||
@@ -167,6 +174,26 @@ export function recalcSheet(doc: SheetDoc): SheetResult {
|
||||
};
|
||||
for (const id of rows.keys()) solve(id, result.계산);
|
||||
for (const id of totals.keys()) solve(id, result.합계);
|
||||
|
||||
// 수 칸에 든 수 아닌 글 — 그 칸 + 그 열을 더하는 합계 칸에 알림
|
||||
const badCount = new Map<string, number>();
|
||||
for (const row of doc.줄) {
|
||||
for (const col of doc.열) {
|
||||
if (!isNumberColumn(col) || formulaOf(row.id, col)) continue;
|
||||
if (typeof inputValue(row.값[col.id]) !== "string") continue;
|
||||
result.오류.push({ 줄: row.id, 열: col.id, 까닭: NOT_A_NUMBER });
|
||||
badCount.set(col.id, (badCount.get(col.id) ?? 0) + 1);
|
||||
}
|
||||
}
|
||||
for (const total of totals.values()) {
|
||||
for (const col of doc.열) {
|
||||
const n = badCount.get(col.id);
|
||||
const formula = n && totalFormula(total, col);
|
||||
if (!formula || !formula.includes(`[${col.id}`)) continue;
|
||||
const 까닭 = `${SKIPPED_PREFIX} ${n} 개는 빼고 더함`;
|
||||
result.오류.push({ 줄: total.id, 열: col.id, 까닭 });
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
pagePlan,
|
||||
type SheetMode,
|
||||
} from "./ui_template_sheet_ops";
|
||||
import { groupDigits, totalFormula } from "./ui_template_sheet_recalc";
|
||||
import { groupDigits, isNotice, totalFormula } from "./ui_template_sheet_recalc";
|
||||
import { st } from "./ui_template_sheet_text";
|
||||
import type { SheetColumn, SheetDoc, SheetResult, SheetRow } from "./ui_template_sheet_types";
|
||||
|
||||
@@ -130,7 +130,7 @@ export function renderSheet(state: RenderState): HTMLElement {
|
||||
const why = errors.get(`${key}|${col.id}`);
|
||||
if (why) {
|
||||
td.classList.add("is-error");
|
||||
td.textContent = st("Error");
|
||||
if (!isNotice(why)) td.textContent = st("Error");
|
||||
td.title = why;
|
||||
} else if (text) td.title = text;
|
||||
if (state.sel?.r === key && state.sel.c === col.id) td.classList.add("is-selected");
|
||||
@@ -240,7 +240,11 @@ export function renderSheet(state: RenderState): HTMLElement {
|
||||
cols.forEach((col, j) => {
|
||||
const td = bodyCell(`t:${total.id}`, col, j, shown(state.result.합계[total.id]?.[col.id]));
|
||||
const formula = totalFormula(total, col);
|
||||
if (formula) td.title = formulaLabel(formula, names);
|
||||
if (formula)
|
||||
td.title = td.classList.contains("is-error")
|
||||
? `${td.title}
|
||||
${formulaLabel(formula, names)}`
|
||||
: formulaLabel(formula, names);
|
||||
tr.append(td);
|
||||
});
|
||||
return tr;
|
||||
|
||||
Reference in New Issue
Block a user