From 0d81acc7d33b87adf16d2f57f717d115e86f4021 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Thu, 24 Sep 2026 11:31:18 +0900 Subject: [PATCH] =?UTF-8?q?fix(m01):=20=EA=B0=92=20=EC=8B=9D=20=EC=9E=91?= =?UTF-8?q?=EC=9D=80=20=EC=88=98=EB=A5=BC=20=EC=9C=A0=ED=9A=A8=EC=88=AB?= =?UTF-8?q?=EC=9E=90=204=20=EC=9E=90=EB=A6=AC=EB=A1=9C=20=C2=B7=20200=20?= =?UTF-8?q?=EC=9B=90=20=EB=AF=B8=EB=A7=8C=20=EB=8B=A8=EA=B0=80=EB=8F=84=20?= =?UTF-8?q?(PLAN=205=EC=9E=A5=204=EB=B0=94=ED=80=B4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01V1MKKZKpUHTPKb513FneU8 --- resources/master_data/scripts/master_text.py | 15 ++++++++++++--- resources/tester/test_m01_text.py | 11 +++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/resources/master_data/scripts/master_text.py b/resources/master_data/scripts/master_text.py index b0d21d40..13ac5665 100644 --- a/resources/master_data/scripts/master_text.py +++ b/resources/master_data/scripts/master_text.py @@ -33,10 +33,13 @@ _CALC_ERRORS = (mf.FormulaError, ArithmeticError, KeyError, TypeError, ValueErro def fmt(value) -> str: - """화면에 보이는 자릿수 그대로 — 소수 넷째 자리까지 · 뒤 0 은 떼고 · 천 단위 쉼표.""" + """보이는 자릿수 — 소수 넷째 자리·유효숫자 4 자리 중 많은 쪽 · 뒤 0 뗌 · 천 단위 쉼표.""" if not isinstance(value, Decimal): return str(value) - cut = value.quantize(PLACES) if value.as_tuple().exponent < -4 else value + places = max(4, 3 - value.adjusted()) if value else 4 # 0.00167 → 0.001667 + cut = value + if value.as_tuple().exponent < -places: + cut = value.quantize(Decimal(1).scaleb(-places)) sign, digits = ("-", -cut) if cut < 0 else ("", cut) whole, _, rest = f"{digits.normalize():f}".partition(".") if "E" in whole or "e" in whole: # 아주 큰 수 — 지수 꼴을 펴서 씀 @@ -51,6 +54,12 @@ def fmt_money(value) -> str: return fmt(value) +def fmt_price(value) -> str: + """단가 — 정수로 보이되 200 원 미만은 반올림 오차가 커서 유효숫자로.""" + small = isinstance(value, Decimal) and abs(value) < 200 + return fmt(value) if small else fmt_money(value) + + # ── 식을 글로 — 값 꼴과 이름 꼴 ──────────────────────────────────────── def render(node, env: dict, master, depth: int = 0) -> str: """식 한 마디 → 값 꼴 글. 값이 정해지는 마디(요소 · 찾기 · 로직 · 이름)는 그 수로 바뀜.""" @@ -377,7 +386,7 @@ def _line_text(item: dict, line: dict, env: dict, master, part: str | None) -> s qty = _times(render(mf.parse(item["수량"]), env, master), item, env, master) money = line["비목"][part] if part else line["금액"] price = line["단가"] if part is None or line["수량"] == 0 else money / line["수량"] - return f"{fmt_money(price)} * {qty} = {fmt_money(money)}" + return f"{fmt_price(price)} * {qty} = {fmt_money(money)}" def _line_names(item: dict, line: dict, env: dict, master, part: str | None, name_of: dict) -> str: diff --git a/resources/tester/test_m01_text.py b/resources/tester/test_m01_text.py index 573aa449..3550758e 100644 --- a/resources/tester/test_m01_text.py +++ b/resources/tester/test_m01_text.py @@ -377,3 +377,14 @@ def test_요소조각_로직_부르기는_이름과_인자_글_날식_없음() - assert "GC000268" not in args["글"] and "(" not in args["글"].split("덤프트럭")[0] labor = lines[0]["요소조각"] assert labor[0]["kind"] == "요소" and labor[0]["글"] == "배관공(수도)" + + +def test_값_식_작은_수는_유효숫자로_보인_수끼리_곱해도_금액과_맞음() -> None: + """GC001064 수량 0.001667 — 넷째 자리로 잘라 456 이 465 로 읽히던 것.""" + files = store.cm.load(folder=REAL) + row = store.cm.master(REAL).logic("GC001064") + got = mt.lines(files, "GC001064", dict(store.cm.시험입력(row))) + first = _all_lines(got)[0]["글"] + assert first == "273,308 * 0.001667 = 456" + assert mt.fmt(Decimal("0.00166667")) == "0.001667" and mt.fmt(Decimal("12.34567")) == "12.3457" + assert mt.fmt_price(Decimal("3.5")) == "3.5" and mt.fmt_price(Decimal("3500.4")) == "3,500"