From de8e1d51c2ebaa9dbfec4413ee1bc653bf782a32 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Thu, 24 Sep 2026 09:10:11 +0900 Subject: [PATCH] =?UTF-8?q?fix(m01):=20=EC=9E=90=EB=8F=99=EA=B0=92=20?= =?UTF-8?q?=EB=B9=88=20=EC=A4=84=EC=97=90=EC=84=9C=20=ED=85=8D=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=88=98=EC=8B=9D=EC=9D=B4=20=EB=A9=88=EC=B6=94?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EC=9D=8C=20=E2=80=94=20=EB=B9=88=20?= =?UTF-8?q?=EC=A4=84=EC=9D=80=20=EB=B9=88=20=EA=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 빈 줄(후보 0 · 값 빈 품목 · 빈 덧줄)은 글 빈 값 · 금액 null 그대로 · 덧줄 식을 다시 셈하지 않음 - 덧줄 합 · 이름 값에 빈 줄을 넣지 않음 - 자동값으로 1,354 전부 텍스트 멈춤 62 → 0 검증: test_m01_text 23개 통과 · 자동값 1,354 멈춤 0 · GC000027 계 = 계산 계. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01ACe3CR4gBWhWUN5p44fLe4 --- resources/master_data/scripts/master_text.py | 15 ++++++++--- resources/tester/test_m01_text.py | 28 ++++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/resources/master_data/scripts/master_text.py b/resources/master_data/scripts/master_text.py index b08fd440..d13492c3 100644 --- a/resources/master_data/scripts/master_text.py +++ b/resources/master_data/scripts/master_text.py @@ -353,7 +353,9 @@ def _shares(line: dict) -> list[tuple[str, Decimal]]: def _line_text(item: dict, line: dict, env: dict, master, part: str | None) -> str: - """「단가 * 수량식 = 금액」 — 비목이 나뉜 로직 줄은 그 비목 몫만.""" + """「단가 * 수량식 = 금액」 — 비목이 나뉜 로직 줄은 그 비목 몫만 · 빈 줄(금액 null)은 빈 글.""" + if line["금액"] is None: + return "" qty = render(mf.parse(item["수량"]), env, master) money = line["비목"][part] if part else line["금액"] price = line["단가"] if part is None or line["수량"] == 0 else money / line["수량"] @@ -370,6 +372,8 @@ def _line_names(item: dict, line: dict, env: dict, master, part: str | None, nam def _extra_text(extra: dict, line: dict, env: dict, master) -> str: + if line["금액"] is None: # 빈 덧줄 — 식을 다시 셈하지 않음 + return "" return f"{render(mf.parse(extra['식']), env, master)} = {fmt_money(line['금액'])}" @@ -520,7 +524,9 @@ def _extras( row: dict, shown: list, made: dict, env: dict, master, name_of: dict, alias_by_name: dict ) -> None: """덧줄 — 앞선 줄까지의 비목 합을 넣고 한 줄씩(식 안의 비목 이름은 그 수로 바뀜).""" - sums = {name: sum((one["금액"] for one in made[name]), Decimal(0)) for name in mf.COST_ITEMS} + sums = { + name: sum((one["금액"] or 0 for one in made[name]), Decimal(0)) for name in mf.COST_ITEMS + } for extra, line in zip(row.get("덧줄") or [], shown): env.update(sums) one = { @@ -531,8 +537,9 @@ def _extras( "조각": _formula_pieces(extra["식"], env, master, name_of, alias_by_name), } made[extra["비목"]].append(one) - sums[extra["비목"]] += line["금액"] - env[extra["이름"]] = line["금액"] + if line["금액"] is not None: # 빈 덧줄은 합·이름 값에 안 넣음 + sums[extra["비목"]] += line["금액"] + env[extra["이름"]] = line["금액"] def _cut_note(row: dict, on: str = "계") -> str: diff --git a/resources/tester/test_m01_text.py b/resources/tester/test_m01_text.py index 82868f86..b00a43d4 100644 --- a/resources/tester/test_m01_text.py +++ b/resources/tester/test_m01_text.py @@ -303,3 +303,31 @@ def test_글_안의_돈은_정수_계산_값은_소수_그대로(client: TestCli first = _all_lines(got)[0] assert first["글"] == "275,790 * 0.22 * 1.31 = 79,483" # 수량·비율은 소수 그대로 assert Decimal(str(first["금액"])) == Decimal("79482.678") # 값은 그대로 + + +def test_자동값으로_모든_로직을_돌려도_멈추지_않는다() -> None: + """빈 줄(후보 0 · 값 빈 품목) — 그 줄만 빈 글 · 로직은 계속(PLAN 3-2).""" + files = store.cm.load(folder=REAL) + master = store.cm.master(REAL) + stops = [] + for rows in master.index.values(): + for key in rows: + if mt.mf.mk.GROUP_OF.get(key[:2]) != "로직": + continue + given = {n: store._dec(v) for n, v in store.auto(key)["값"].items()} + try: + mt.lines(files, key, given) + except mt._CALC_ERRORS as e: + stops.append((key, str(e))) + assert not stops, stops[:3] + + +def test_빈_줄은_빈_글이고_소계는_빈_줄_빼고_더한다(client: TestClient) -> None: + key = "GC000027" + given = {n: str(v) for n, v in store.auto(key)["값"].items()} + got = client.post("/api/m01/text", json={"key": key, "inputs": given}).json() + calc = client.post("/api/m01/calc", json={"key": key, "inputs": given}).json() + assert got["ok"] is True + blank = [one for one in _all_lines(got) if one["금액"] is None] + assert blank and all(one["글"] == "" and one["까닭"] for one in blank) + assert Decimal(str(got["계"])) == Decimal(str(calc["sums"]["계"]))