diff --git a/B09_Estimation/B09_Estimation_Sangeun_Text.py b/B09_Estimation/B09_Estimation_Sangeun_Text.py index 6d40d7aac..cd2deffa1 100644 --- a/B09_Estimation/B09_Estimation_Sangeun_Text.py +++ b/B09_Estimation/B09_Estimation_Sangeun_Text.py @@ -5,7 +5,8 @@ 중간 「Q = 이름식 = 값」(작업량 Q = ROUND 2 — `Sangeun._env`) · 줄 「보통인부 : 172,068 * 0.022 = 3,785.4」(단가 * 수량식 값 꼴[ / 배][ * 율 %] = 금액) · 중기 = 노무비 · 재료비 · 경비 세 줄 · 하위 산근 = 「산근 n호표 단가 * 수량」 · - % 덧줄 = 「이름 : 노무비의 n% = 밑수 * n % = 금액」 · 끝 = 「계 : 노무비 + 재료비 + 경비 = 합」. + % 덧줄 = 「이름 (노무비의 %) : 밑수 * n % = 금액」(규격 = 149-4 `pct_extras`) · + 끝 = 「계 : 노무비 + 재료비 + 경비 = 합」. 식 풀이 = 엔진 `master_text` 낮은 부품(render · _names · fmt) · 단가 = 인자 `price_of(key)`(B09 굳힌 단가 · 엔진 단가 글은 안 씀) · 단가 없음 = 「단가 없음」 글. 끝수 = 엑셀 자리 — 줄 ROUNDDOWN 1 · 계 비목마다 ROUNDDOWN 0(`Sangeun._money` · `block_totals`). @@ -21,6 +22,7 @@ from typing import Any from B09_Estimation import B09_Estimation_Sangeun as sg from M01_MasterData import M01_MasterData_Store as m01 +from M02_MasterTemplete import M02_Labor_Doc as labor_doc mf = sg.mf mtx = m01.mtx @@ -141,37 +143,24 @@ def _logic_lines(whole, item, env, qty_text, q, price_of) -> list[tuple[str, dic return [(text, amounts)] -def _pct(extra: dict) -> str: - """% 덧줄 이름 — 식 「X * Y / 100」 의 X 가 이름이면 「X의 Y%」 · 그 밖 빈 글.""" - try: - node = mf.parse(extra.get("식") or "") - except mf.FormulaError: - return "" - if not (node[0] == "bin" and node[1] == "/" and node[3] == ("num", Decimal(100))): - return "" - inner = node[2] - if inner[0] != "bin" or inner[1] != "*" or inner[3][0] != "num": - return "" - base = inner[2] - if base[0] == "name": - word = base[1] - elif base[0] == "field" and base[1] == ("name", "줄"): - word = base[2] - else: - return "" - return f"{word}의 {mtx.fmt(inner[3][1])}%" +def _pct_specs(whole, row, env) -> list[str]: + """% 덧줄마다 규격(「노무비의 %」 · 149-4 `M02_Labor_Doc.pct_extras`) · % 아닌 덧줄 = 빈 글.""" + head = [{} for _ in row.get("호표", [])] + tail = [{} for _ in row.get("덧줄", [])] + got = labor_doc.pct_extras(whole, row, head + tail, dict(env))[len(head) :] + return [str(one.get("규격") or "") for one in got] def _extra_lines(whole, row, env, amounts: list[dict]) -> list[tuple[str, dict]]: """% 줄(덧줄) — 밑수 = 그때까지 B09 비목 합(`Sangeun._extras` 와 같음) · 금액 ROUNDDOWN 1.""" out: list[tuple[str, dict]] = [] - for extra in row.get("덧줄", []): + specs = _pct_specs(whole, row, env) + for extra, spec in zip(row.get("덧줄", []), specs): part = sg.COST_PART.get(extra.get("비목"), "material") sums = { name: sg.total([a.get(p, 0.0) for a in amounts]) for name, p in sg.COST_PART.items() } - label = _pct(extra) - head = f"{extra['이름']} : {label + ' = ' if label else ''}" + head = f"{extra['이름']}{f' ({spec})' if spec else ''} : " if any(v is None for v in sums.values()): out.append((f"{head}밑수 빈칸", {part: None})) amounts.append({part: None}) diff --git a/resources/tester/test_155_5_sangeun_text.py b/resources/tester/test_155_5_sangeun_text.py index 249676ff7..314a1902d 100644 --- a/resources/tester/test_155_5_sangeun_text.py +++ b/resources/tester/test_155_5_sangeun_text.py @@ -3,6 +3,7 @@ - 엑셀 6호표 뒷채움: 「보통인부 : 165,545 * 0.018 = 2,979.8」 · 중기 노 · 재 · 경 세 줄 · 「계 : 노무비 6,878 + 재료비 538 + 경비 912 = 8,328」(줄 ROUNDDOWN 1 · 계 ROUNDDOWN 0) - 엑셀 7호표 막자갈채집: 작업량 Q = ROUND 2 중간 줄 · 「55,700 * 1 / 21.59 * 2 = 5,159.7」 +- % 덧줄 = 「공구손료 (노무비의 %) : 6,878.8 * 3 % = 206.3」(규격 = 149-4 `pct_extras`) - 단가 없음 = 「단가 없음」 글 · 계 그 비목 빈칸 · 글 속 금액 = 산근 풀이(`Sangeun.Solver`) 줄 값 """ @@ -68,6 +69,16 @@ def test_엑셀_7호표_작업량_Q_중간_줄(): assert "굴착기(무한궤도) 0.2㎥ 노무비 : 55,700 * 1 / 21.59 * 2 = 5,159.7" in got +def test_퍼센트_덧줄_노무비의_퍼센트(): + tool = {"이름": "공구손료", "식": "노무비 * 3 / 100", "비목": "경비"} + whole = _whole(_excel("0.018", "0.07", extras=[tool])) + got = st.render_sangeun_text(whole, BASE, GIVEN, _prices({"LB000002": 165545})) + assert got[-2:] == [ + "공구손료 (노무비의 %) : 6,878.8 * 3 % = 206.3", # 밑수 = 인력 + 중기 노무비 + "계 : 노무비 6,878 + 재료비 538 + 경비 1,119 = 8,535", + ] + + def test_배_켜쌓기율_꼬리_단가_없음(): whole = _whole(_excel("0.018", "0.07")) got = st.render_sangeun_text(whole, BASE, GIVEN, _prices({}), times=10, rate=90)