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:
2026-11-07 17:44:49 +09:00
co-authored by Claude Sonnet 5.5
parent 90478a43c3
commit 47f260e012
8 changed files with 294 additions and 85 deletions
@@ -4,10 +4,10 @@
(`M02_Sangeun_Calc.calc_doc` = 화면 · 저장 · B09 가 쓰는 한 벌)을 얹어 화면 금액 = SK 금액
(셈은 안 바꿈 · 보이기만):
합계 = 셈 답 `기준`(노 · 재 · 경 · 계 · 1 단위) — 바탕 줄만 셈한 합계 대신
소계 = 셈 답 `줄` 한 줄마다 한 행 {cid, 갈래(자원 | 블록 | 덧줄 | 율), 이름, 상태, 수량,
노 · 재 · 경 · 계, 덮개?, 원래?, 까닭?, 나무?, 합밖?, 밖금액?} +
끝수(합계 − 행 합 · 까닭 글)
합밖 줄(엔진이 합계에서 뺌) = 행 금액 0 + 밖금액
소계 = 셈 답 `줄` → 돈 묶음(801-3 · `master_equation_group`) — 더한 · 부른 단가산출(갈래 블록) 하나씩
{cid, 이름, 상태, 수량, 노 · 재 · 경 · 계, 덮개?, 원래?, 까닭?, 나무?} + 직접 줄(인력 · 재료 · 기계 ·
덧줄 · 율) 한 묶음 {갈래 직접, 이름, 노 · 재 · 경 · 계, 낱줄?(더함 · 덮음 줄)} +
끝수(합계 − 묶음 합 · 까닭 글) · 밖(합계 밖 줄 — 엔진이 합계에서 뺌 · 이름 · 밖금액)
상태 = 바탕(그대로) · 덮음(품 · 단가 · 상수 · 표값 · 식 덮개 · 변수줄) ·
더함(더한줄) · 뺌(뺀줄 · 금액 0)
나무 = 더한 기초(더한줄 로직)의 수식(세부 덮개 · 뺀줄 얹은 줄) — 화면은 접힘 블록
@@ -31,6 +31,7 @@ SUMS = (*COSTS, "계")
_ADD = re.compile(r"^add:#(\d+)$")
_HEAD = {"labor": "자원", "material": "자원", "machine": "자원", "logic": "블록", "pct": "덧줄"}
_ADD_KIND = {"인력": "자원", "재료": "자원", "기계": "자원"}
_WORD = {"labor": "인력", "material": "재료", "machine": "기계"} # 직접 묶음 이름(801-3)
def _d(value: Any) -> Decimal:
@@ -69,6 +70,15 @@ def _kind(cid: str, adds: list) -> str:
return _ADD_KIND.get(str(one.get("종류")), "블록")
def _word(cid: str, adds: list) -> str:
"""직접 줄의 자원 갈래 말 — 인력 · 재료 · 기계(그 밖 = 빈 글)."""
hit = _ADD.match(cid)
if not hit:
return _WORD.get(cid.split(":", 1)[0], "")
one = adds[int(hit[1])] if int(hit[1]) < len(adds) else {}
return str(one.get("종류")) if one.get("종류") in _ADD_KIND else ""
def _row(cid: str, kind: str, name: str, state: str, parts: dict | None, **more: Any) -> dict:
cost = {k: _d((parts or {}).get(k)) for k in COSTS}
return {
@@ -112,6 +122,7 @@ def rows(doc: dict, ans: dict, tree: Any, out_ids: set[str] = frozenset()) -> li
if outside
else None,
수량=line.get("수량"),
종류=_word(cid, adds),
덮개=cover,
원래=info.get("원래") if state == "덮음" else None,
까닭=line.get("까닭") or info.get("까닭"),
@@ -209,6 +220,8 @@ def attach(
eq: dict, doc: dict, whole: Any, base: dict, inputs: dict | None, ans: dict | None
) -> dict:
"""수식 `eq` 에 SK 셈 답 합계 · 소계 · 표지를 얹음(제자리) — 답 없음 = `답까닭` 만."""
import master_equation_group as grp # M01 이 길을 둔 뒤
if ans is None:
return eq | {"답까닭": "단가산출 셈 답이 서지 않음 — 바탕 줄 합계를 보임"}
_mark(eq, base, ans, doc)
@@ -219,8 +232,9 @@ def attach(
lambda k: _basic_tree(whole, doc, adds[k], inputs) if k >= 0 else None,
_outside(base),
)
got = grp.bundles(table) # 801-3 묶음 — 부른 · 더한 단가산출 하나씩 + 직접 줄 한 묶음
now = total(ans) or {}
rest = {k: now[k] - sum((r[k] for r in table), Decimal(0)) for k in SUMS}
rest = {k: now[k] - sum((r[k] for r in got["줄"]), Decimal(0)) for k in SUMS}
eq["합계"] = now
eq["소계"] = {"줄": table, "끝수": rest, "끝수까닭": _why(ans)}
eq["소계"] = {"줄": got["줄"], "밖": got["밖"], "끝수": rest, "끝수까닭": _why(ans)}
return eq