feat(M01): 사람이 읽는 식 줄 API — 비목 묶음마다 「단가 * 수량식 = 금액」 과 소계

- 엔진 `resources/master_data/scripts/master_text.py` — 식 마디를 그 자리에서 나온 수로 풀어 한 줄 글로(찾기·로직·만약·중간 값·요소는 글자로 안 나감 · 「… / 100」 은 「… %」 · 수는 화면과 같은 자릿수)
- `POST /text` — 몸은 `/calc` 와 같음 · 답 `{ok, groups: [{비목, 줄, 소계, 끝수}], 계}` · 멈추면 까닭 한 줄
- 중간 값·끝수는 따로 줄로 안 보임(끝수는 소계 옆 한 마디) · 덧줄은 그 비목의 한 줄 · 하위 로직 줄은 비목마다 한 줄
- 단가 0 이거나 안 쓰는 줄은 까닭 한 마디
- 시험 `resources/tester/test_m01_text.py` 6 통과(13-4-1 · 12-4 · 12-17-1 · 계가 `/calc` 와 같음) · M01 시험 41 통과 · typecheck 통과 · 일괄 시험 계산 1,291 그대로

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JgUhN55Z3BCYhUJTjwTNfj
This commit is contained in:
2026-09-21 19:37:55 +09:00
co-authored by Claude Opus 5
parent f062e51e0d
commit d12f471c93
5 changed files with 323 additions and 0 deletions
+13
View File
@@ -20,6 +20,7 @@ sys.path.insert(0, str(SCRIPTS))
import check_master as cm # noqa: E402
import master_keys as mk # noqa: E402
import master_text as mtx # noqa: E402
from M01_MasterData.M01_MasterData_Store_Shape import dump, keep_shape # noqa: E402
@@ -540,6 +541,18 @@ def calc(key: str, inputs: dict, row: dict | None = None, file: str | None = Non
}
def text(key: str, inputs: dict, row: dict | None = None, file: str | None = None):
"""사람이 읽는 식 줄 — 비목 묶음마다 「이름 : 단가 * 수량식 = 금액」 과 소계(`master_text`)."""
given = {k: _dec(v) for k, v in inputs.items()}
files = cm.load(folder=FOLDER)
if row is not None:
key = _swap(files, str(row.get("") or key), row, file)
try:
return mtx.lines(files, key, given)
except (cm.mf.FormulaError, ArithmeticError, KeyError, TypeError, ValueError) as e:
return {"ok": False, "reason": str(e) or type(e).__name__}
# ── 저장 ──────────────────────────────────────────────────────────────
def _dec(v):
"""화면이 보낸 수 → Decimal(파일을 읽은 모양과 같게)."""