feat(801-3): 수식 탭 소계 · 합계 묶음 다시 — 돈 묶음 하나 = 머리 없음 · 계 / 둘 이상 = 소계 / 단가산출 둘 이상 = 합계 · 안쪽 말 0
- 묶음 한 곳 master_equation_group.bundles: 부른 · 더한 단가산출 하나씩 + 직접 줄(인력 · 재료 · 기계 · 덧줄) 한 묶음 · 돈 없는 줄 · 뺀 줄 = 묶음 아님 · 합계 밖 줄 = 밖 글 - M01 subtotals · M02 M02_Sangeun_Equation_Full 같은 꼴 · 끝수 셈 그대로 · 금액 전후 같음 - 부품 ui_template_equation_sum: 자기 자원 · 덧줄 글 0 · 묶음 하나 = 머리 없음 · 계 · 둘 이상 = 가. 머리 · 소 계 · 단가산출 둘 이상 = 합계 - 옛 시험 기대 고침: test_799_1(갈래 자원 · 덧줄 → 직접 한 묶음) · test_796_2c(줄마다 한 행 → 묶음 · 낱줄 · 밖) — 묶음 꼴이 바뀜 - 새 test_801_3_groups 5 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -45,7 +45,7 @@ export interface EqNode {
|
||||
사슬?: boolean;
|
||||
}
|
||||
|
||||
/** 단가산출 소계 줄(799-1) — 갈래 블록 | 자원 | 덧줄 | 율 · M02 SK(796-2c) 는 셈 답 한 줄마다 + 상태 */
|
||||
/** 단가산출 소계 묶음(799-1 · 801-3) — 갈래 블록(부른 · 더한 단가산출) | 직접(인력 · 재료 · 기계 · 덧줄 한 묶음) · M02 SK 는 상태 */
|
||||
export interface EqSumRow {
|
||||
갈래: string;
|
||||
이름: string;
|
||||
@@ -61,9 +61,8 @@ export interface EqSumRow {
|
||||
/** 덮음 · 뺌 — 고치기 전 금액 */
|
||||
원래?: unknown;
|
||||
까닭?: string;
|
||||
/** 합계 밖 줄(엔진이 비목 합에서 뺌) — 행 금액은 0 · 밖금액에 원래 금액 */
|
||||
합밖?: boolean;
|
||||
밖금액?: unknown;
|
||||
/** 직접 묶음 속 더한 · 덮은 줄(M02 SK) */
|
||||
낱줄?: { 이름: string; 상태: string; 계: unknown; 원래?: unknown }[];
|
||||
/** 더한 기초의 수식(접힘 블록) */
|
||||
나무?: EqTree | null;
|
||||
/** 더한 기초 차례(M02 SK 더한줄 · 연결 끊기 796-2e) */
|
||||
@@ -86,8 +85,13 @@ export interface EqTree {
|
||||
줄: EqLine[];
|
||||
변수표: EqVarRow[];
|
||||
합계: Record<string, unknown>;
|
||||
/** 799-1 — 하위 로직 블록마다 노 · 재 · 경 소계 + 끝수(합계 − 줄 합) · 결과 로직은 없음 */
|
||||
소계?: { 줄: EqSumRow[]; 끝수: Record<string, unknown>; 끝수까닭?: string };
|
||||
/** 799-1 · 801-3 — 묶음마다 노 · 재 · 경 소계 + 끝수(합계 − 묶음 합) · 합계 밖 줄 · 결과 로직은 없음 */
|
||||
소계?: {
|
||||
줄: EqSumRow[];
|
||||
끝수: Record<string, unknown>;
|
||||
끝수까닭?: string;
|
||||
밖?: { 이름: string; 밖금액: unknown }[];
|
||||
};
|
||||
/** M02 SK — 셈 답이 안 서서 바탕 줄 합계를 보일 때의 까닭 */
|
||||
답까닭?: string;
|
||||
/** 800-2 — 오른쪽 변수 정의 영역(고른 옵션 갈래만 · 같은 기호 한 줄 · 값 · 단위 · DB 열쇠 없음) */
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/* =============================================================================
|
||||
* ui_template_equation_sum.ts
|
||||
* 부품 「수식」 셋째 파일(799-1 · 796-2c · 800-4 · 요구 655-1 · 661 · 요구서_655 2 장 4) — 단가산출 소계 · 합계(보이기만 · 금액 셈 그대로).
|
||||
* 묶음 = 기준 단가산출마다(하위 로직 블록 · 자기 자원 · 덧줄) 「가. 이름」 머리 → 노무비 · 재료비 · 경비 줄 → 소 계(노 · 재 · 경 · 계 칸)
|
||||
* 맨 끝 「소 계」 묶음 = 묶음 소계를 더한 식(노무비 : 2,299.4 + 5,162 = 7,461.4) → 끝수(합계 − 묶음 합 · 0 이면 줄 없음) → 합계(엔진 비목 합)
|
||||
* 값 = 서버 `소계`(M01 `master_equation.subtotals` · M02 `M02_Sangeun_Equation_Full` = SK 셈 답) · 소계가 없으면(결과 로직) 아무것도 안 그림
|
||||
* M02 SK 묶음 = 상태 알약(더함 · 덮음 · 뺌) · 덮음 갈래 · 원래 금액 · 수량 · 더한 기초는 접힘 수식 블록 · 뺌 = 흐리게 · 금액 0
|
||||
* 자리 = 하위 로직 블록 묶음은 그 블록 식 줄 바로 아래(`inline` · 줄 열쇠 = 블록 이름 · `createEqChain` 의 `after`) · 나머지(자기 자원 · 덧줄 · 더한 기초) · 맨 끝 소 계 · 끝수 · 합계는 `tail`
|
||||
* 부품 「수식」 셋째 파일(799-1 · 796-2c · 800-4 · 801-3 · 요구 655-1 · 661 · 662-3 · 요구서_655 2 장 4) — 단가산출 소계 · 합계(보이기만 · 금액 셈 그대로).
|
||||
* 묶음 = 부른 · 더한 단가산출 하나씩 + 직접 줄(인력 · 재료 · 기계 · 덧줄) 한 묶음 — 값 = 서버 `소계`(M01 `master_equation.subtotals` · M02 `M02_Sangeun_Equation_Full` · 꼴 = `master_equation_group`) · 소계가 없으면(결과 로직) 아무것도 안 그림
|
||||
* 묶음 하나뿐 = 머리 없음 · 노무비 · 재료비 · 경비 줄 → 「계」 한 줄(끝수가 있으면 끝수 줄 먼저)
|
||||
* 묶음 둘 이상 = 묶음마다 「가. 이름」 머리 → 노무비 · 재료비 · 경비 줄 → 소 계 · 맨 끝 묶음 소계를 더한 식 → 끝수 → 합계(단가산출이 둘 이상) 아니면 계
|
||||
* M02 SK = 상태 알약(더함 · 덮음) · 원래 금액 · 더한 기초는 접힘 수식 블록(더한 기초 하나뿐이어도 머리 줄은 둠 — 연결 끊기 단추 자리) · 합계 밖 줄은 글 한 줄
|
||||
* 자리 = 하위 로직 블록 묶음은 그 블록 식 줄 바로 아래(`inline` · 줄 열쇠 = 블록 이름 · `createEqChain` 의 `after`) · 나머지(직접 줄 · 더한 기초) · 맨 끝 합계 · 끝수는 `tail`
|
||||
* 쓰는 법 = `createEqSum(tree, open?, hooks?, lineKeys?)` → { inline, tail } | null — `lineKeys` = 수식 영역 호표 줄 열쇠들 · `open` = 펼친 블록 이름표 모음 ·
|
||||
* `hooks` = 페이지가 더한 기초 줄 머리 단추(`head`) · 접힘 속(`body`)을 바꿔 끼움(796-2e 연결 끊기)
|
||||
* ========================================================================== */
|
||||
@@ -18,7 +18,6 @@ import { createEqChain } from "./ui_template_equation_line";
|
||||
const COLS = ["노무비", "재료비", "경비", "계"] as const;
|
||||
const PARTS = ["노무비", "재료비", "경비"] as const;
|
||||
type Col = (typeof COLS)[number];
|
||||
const KIND: Record<string, string> = { 자원: "자기 자원", 덧줄: "덧줄" };
|
||||
const TONE: Record<string, BadgeTone> = { 더함: "success", 덮음: "warning", 뺌: "neutral" };
|
||||
const ORDER = "가나다라마바사아자차카타파하";
|
||||
|
||||
@@ -66,17 +65,23 @@ const part = (label: string, text: string): HTMLElement => {
|
||||
};
|
||||
|
||||
/** 기준 단가산출 묶음 하나 — 「가. 이름」 머리(+ 상태 · 접힘 수식) → 노 · 재 · 경 줄 → 소 계 */
|
||||
function group(r: EqSumRow, open: Set<string>, at: number, hooks?: EqSumHooks): HTMLElement {
|
||||
function group(
|
||||
r: EqSumRow,
|
||||
open: Set<string>,
|
||||
at: number,
|
||||
heads: boolean,
|
||||
hooks?: EqSumHooks,
|
||||
): HTMLElement {
|
||||
const box = node("div", "ui-eq__grp");
|
||||
box.dataset.eqSumItem = r.갈래;
|
||||
box.dataset.eqSumState = r.상태 ?? "바탕";
|
||||
const name = node("div", "ui-eq__grp-head");
|
||||
const title = r.갈래 === r.이름 ? (KIND[r.갈래] ?? r.이름) : r.이름;
|
||||
const title = r.이름;
|
||||
const tree = r.나무;
|
||||
const id = `sum:${at}:${r.이름}`;
|
||||
const body = node("div", "ui-eq__sum-tree");
|
||||
body.hidden = true;
|
||||
name.append(node("span", "ui-eq__grp-mark", `${mark(at)}.`));
|
||||
if (heads) name.append(node("span", "ui-eq__grp-mark", `${mark(at)}.`));
|
||||
if (tree) {
|
||||
const head = createButton({
|
||||
label: title,
|
||||
@@ -107,35 +112,32 @@ function group(r: EqSumRow, open: Set<string>, at: number, hooks?: EqSumHooks):
|
||||
name.append(createBadge(`${r.상태}${detail}`, TONE[r.상태] ?? "neutral", { size: 18 }));
|
||||
}
|
||||
name.append(...(hooks?.head?.(r, at) ?? []));
|
||||
const out = !!r.합밖;
|
||||
if (out) name.append(createBadge("합 밖", "neutral", { size: 18 }));
|
||||
const notes: string[] = [];
|
||||
if (out) notes.push(`합계에 안 듦 ${eqText(r.밖금액) || "0"}`);
|
||||
if (r.상태 === "더함" && r.수량 !== undefined && r.수량 !== null)
|
||||
notes.push(`× ${eqText(r.수량)}`);
|
||||
if (r.원래 !== undefined && r.원래 !== null && r.상태 !== "더함")
|
||||
notes.push(`원래 ${eqText(r.원래)}`);
|
||||
if (r.까닭) notes.push(r.까닭);
|
||||
if (notes.length) name.append(node("span", "ui-eq__sum-was", notes.join(" · ")));
|
||||
box.append(name, body);
|
||||
const shown = heads || !!tree || notes.length > 0 || (!!r.상태 && r.상태 !== "바탕");
|
||||
if (shown) box.append(name, body);
|
||||
for (const k of PARTS) if (num(r[k]) !== 0) box.append(part(k, eqText(r[k])));
|
||||
const total = row("소계", "소 계", (k) => r[k]);
|
||||
total.classList.add("ui-eq__grp-total");
|
||||
if (out) total.dataset.eqSumOut = "1";
|
||||
box.append(total);
|
||||
for (const x of r.낱줄 ?? []) {
|
||||
const was = x.원래 !== undefined && x.원래 !== null ? ` · 원래 ${eqText(x.원래)}` : "";
|
||||
box.append(part(`${x.이름} · ${x.상태}`, `${eqText(x.계) || "0"}${was}`));
|
||||
}
|
||||
if (heads) {
|
||||
const total = row("소계", "소 계", (k) => r[k]);
|
||||
total.classList.add("ui-eq__grp-total");
|
||||
box.append(total);
|
||||
}
|
||||
return box;
|
||||
}
|
||||
|
||||
/** 맨 끝 「소 계」 묶음 — 묶음 소계를 더한 식 */
|
||||
function final(rows: EqSumRow[], at: number): HTMLElement {
|
||||
/** 맨 끝 — 묶음 소계를 더한 식(노무비 : 2,299.4 + 5,162 = 7,461.4) */
|
||||
function final(rows: EqSumRow[]): HTMLElement {
|
||||
const box = node("div", "ui-eq__grp");
|
||||
box.dataset.eqSumItem = "소계";
|
||||
const head = node("div", "ui-eq__grp-head");
|
||||
head.append(
|
||||
node("span", "ui-eq__grp-mark", `${mark(at)}.`),
|
||||
node("span", "ui-eq__sum-label", "소 계"),
|
||||
);
|
||||
box.append(head);
|
||||
box.dataset.eqSumItem = "합";
|
||||
for (const k of PARTS) {
|
||||
const adds = rows.map((r) => num(r[k])).filter((v) => v !== 0);
|
||||
if (!adds.length) continue;
|
||||
@@ -160,21 +162,27 @@ export function createEqSum(
|
||||
lineKeys: string[] = [],
|
||||
): EqSumParts | null {
|
||||
const sub = tree.소계;
|
||||
if (!sub?.줄.length) return null;
|
||||
if (!sub) return null;
|
||||
const inline = new Map<string, HTMLElement>();
|
||||
const box = node("div", "ui-eq__sum");
|
||||
box.dataset.eqSum = "";
|
||||
const heads = sub.줄.length >= 2; // 묶음 하나뿐 = 머리 · 소계 없음
|
||||
const word = sub.줄.filter((r) => r.갈래 === "블록").length >= 2 ? "합계" : "계"; // 합계 = 단가산출 둘 이상
|
||||
sub.줄.forEach((r, at) => {
|
||||
const one = group(r, open, at, hooks);
|
||||
const one = group(r, open, at, heads, hooks);
|
||||
if (r.갈래 === "블록" && lineKeys.includes(r.이름) && !inline.has(r.이름))
|
||||
inline.set(r.이름, one);
|
||||
else box.append(one);
|
||||
});
|
||||
box.append(final(sub.줄, sub.줄.length));
|
||||
if (heads) box.append(final(sub.줄));
|
||||
const rest = sub.끝수;
|
||||
const odd = COLS.some((k) => Number(rest[k] ?? 0) !== 0);
|
||||
if (odd) box.append(row("끝수", "끝수", (k) => rest[k]));
|
||||
box.append(row("합계", "합계", (k) => tree.합계[k]));
|
||||
box.append(row(word, word, (k) => tree.합계[k]));
|
||||
for (const x of sub.밖 ?? [])
|
||||
box.append(
|
||||
node("div", "ui-eq__sum-note", `합계에 안 듦 · ${x.이름} ${eqText(x.밖금액) || "0"}`),
|
||||
);
|
||||
const note = [odd ? sub.끝수까닭 : "", tree.답까닭].filter(Boolean).join(" · ");
|
||||
if (note) box.append(node("div", "ui-eq__sum-note", note));
|
||||
return { inline, tail: box };
|
||||
|
||||
Reference in New Issue
Block a user