diff --git a/M02_MasterTemplete/M02_Item_Sangeun.py b/M02_MasterTemplete/M02_Item_Sangeun.py index f84b1fd27..9f4b9df23 100644 --- a/M02_MasterTemplete/M02_Item_Sangeun.py +++ b/M02_MasterTemplete/M02_Item_Sangeun.py @@ -114,7 +114,8 @@ def _freeze_lines(rid: str, doc: dict, whole: Any, read: Any, taken: set[str]) - for n in _patched(doc): one = lines[n] try: - got = calc.resolve(sd.clean(patch.as_doc(one, whole), None, whole), whole, read, (rid,)) + doc_ = sd.clean(patch.as_doc(one, whole), None, whole) + got = calc.resolve(doc_, whole, read, (rid,), line=True) # 일위대가 기본 별도계상 row = {**got["줄"], "호표": [dict(x) for x in got["줄"]["호표"]]} calc._bundles(row, whole, Expand(whole, read), (rid,)) # 고정 더한줄 = 값묶음 except sd.DocError: diff --git a/M02_MasterTemplete/M02_Labor_Patch.py b/M02_MasterTemplete/M02_Labor_Patch.py index 87cd6a61a..f5fdf899f 100644 --- a/M02_MasterTemplete/M02_Labor_Patch.py +++ b/M02_MasterTemplete/M02_Labor_Patch.py @@ -100,7 +100,7 @@ def run(whole: Any, one: dict, read: Any = None) -> dict[str, Any]: try: doc = sd.clean(as_doc(one, whole), None, whole) - return calc.run(doc, whole, None, read or refs.read_ref) + return calc.run(doc, whole, None, read or refs.read_ref, line=True) except sd.DocError as e: return {"기준": None, "까닭": str(e)} diff --git a/M02_MasterTemplete/M02_Sangeun_Calc.py b/M02_MasterTemplete/M02_Sangeun_Calc.py index e94909b07..63de29f2d 100644 --- a/M02_MasterTemplete/M02_Sangeun_Calc.py +++ b/M02_MasterTemplete/M02_Sangeun_Calc.py @@ -15,6 +15,8 @@ SK 단위 환산(728-2) = 배 = 바탕 기준단위(「100본」) → SK 단위( 「식」 · 빈 단위 = 앞 수 뗀 바탕 단위 = 옛 셈) · 다른 성격 = 문서 `환산` {계수 | 단위중량 {종별, 형상}} · 없으면 셈 막음(까닭) · 화면 셈 길 줄 · 계 = SK 1 단위 몫(`per_unit`). +바탕 재료(747-1) = 별도계상 빈 칸 = 직접계산(M01 기초처럼 재료비에 듦 · 「별도계상」 고른 줄만 밖) · +일위대가 덮개 줄(`line` · `M02_Labor_Patch.run`)은 일위대가 기본(222-1 · 162-1) 그대로. """ from __future__ import annotations @@ -126,7 +128,12 @@ def _base_of(doc: dict, whole: Any, read: Any, trail: tuple[str, ...]) -> dict[s def resolve( - doc: dict, whole: Any, read: Any = refs.read_ref, trail: tuple = (), given: dict | None = None + doc: dict, + whole: Any, + read: Any = refs.read_ref, + trail: tuple = (), + given: dict | None = None, + line: bool = False, ) -> dict[str, Any]: """SK 문서 → 고친 줄 `{줄, 고름, 자재표시, 정체, 짝잃음, 입력값, 켜쌓기율, 배, 바탕판}` · `trail` = 지나온 참조 이름표(순환 · 깊이 422) · `given` = 공통 옵션(더한줄 로직 입력).""" @@ -135,6 +142,8 @@ def resolve( doc, lack = _fill(doc, whole, common) got = sd.patched(base["줄"], doc, base["고름"], base["자재표시"]) items = got["줄"]["호표"] + if not line: + _direct(got["자재표시"], items) for at, cid in enumerate(got["정체"][: len(items)]): hit = re.match(r"^add:#(\d+)$", cid) if hit and int(hit[1]) in lack: @@ -155,6 +164,13 @@ def resolve( } +def _direct(marks: dict, items: list) -> None: + """SK 재료 줄 별도계상 빈 칸 = 직접계산(747-1 · 제자리) — 다른 두 칸은 그대로.""" + for n, item in enumerate(items): + if item.get("종류") == "재료": + marks[str(n)] = {"별도계상": "직접계산", **labor.marks(marks.get(str(n)))} + + # ── 못 셈 줄 = 그 줄만 빠짐(725-1 · 725-2) ───────────────────────────────── NO_QTY = "수량 없음" # `M02_Labor_Expand.NO_QTY` 와 같은 글(가져오기 둘레 피함) NO_QTY_WHY = "수량 없음 — 쓰는 자리에서 연결" @@ -311,13 +327,14 @@ def run( expand: Any = None, trail: tuple = (), detail: bool = False, + line: bool = False, ) -> dict[str, Any]: """SK 셈(저장 안 함) — `run_line` 답 + `{1단위, 배, 정체, 짝잃음, 바탕판}` · 줄마다 `cid` · 고친 칸 `덮개`(칸 이름) · `원값`. 틀린 문서 · 순환 = `sd.DocError` · - `detail` = 둘째 기초 세부 펼침(`_detail` · 화면 셈 길만).""" + `detail` = 둘째 기초 세부 펼침(`_detail` · 화면 셈 길만) · `line` = 일위대가 덮개 줄.""" from M02_MasterTemplete.M02_Labor_Expand import Expand - got = resolve(doc, whole, read, trail, given) + got = resolve(doc, whole, read, trail, given, line) row = {**got["줄"], "호표": [dict(x) for x in got["줄"]["호표"]]} _bundles(row, whole, expand or Expand(whole, read), trail) head = {k: got[k] for k in ("정체", "짝잃음", "바탕판")} | {"배": got["배"]} diff --git a/resources/tester/test_747_1_sk_material.py b/resources/tester/test_747_1_sk_material.py new file mode 100644 index 000000000..5657796ff --- /dev/null +++ b/resources/tester/test_747_1_sk_material.py @@ -0,0 +1,50 @@ +"""SK 바탕 재료 = M01 기초 그대로 셈(PLAN 747-1) — 별도계상 빈 칸 = 직접계산 · 고른 줄만 밖 · +일위대가 덮개 줄(`M02_Labor_Patch.run`)은 일위대가 기본(별도계상) 그대로.""" + +import pytest + +from M01_MasterData import M01_MasterData_Store as m01 +from M02_MasterTemplete import M02_Labor_Doc as labor +from M02_MasterTemplete import M02_Labor_Patch as patch +from M02_MasterTemplete import M02_Sangeun_Calc as calc +from M02_MasterTemplete import M02_Sangeun_Doc as sd + +FORM = "GC000211" # 6-3-1 합판거푸집 — 합판 1.03 · 각재 0.038 · 폼타이 0 · 소모자재 = 재료비 % +PLY = "material:합판(내수 12㎜)#0" + + +def _whole(): + return m01.loaded()[1] + + +def _run(**doc): + whole = _whole() + clean = sd.clean({"키": "SK-000001", "이름": "시험", "단위": "㎡", **doc}, None, whole) + return labor._json(calc.run(clean, whole)) + + +def test_base_materials_count_like_m01(): + m01_ = labor._json(labor.run_line(_whole(), FORM, None)) + got = _run(바탕={"로직": FORM}) + assert got["기준"]["재료비"] == pytest.approx(m01_["기준"]["재료비"]) + assert got["기준"]["계"] == pytest.approx(m01_["기준"]["계"]) + qty = {x["cid"]: x["수량"] for x in got["줄"]} + assert qty[PLY] == pytest.approx(1.03) + assert qty["material:폼타이 12.7㎜#0"] == 0 # M01 수량 0 = 0 그대로 + + +def test_cover_and_explicit_apart(): + base = _run(바탕={"로직": FORM}) + more = _run(바탕={"로직": FORM}, 덮개={PLY: {"qty": 2}}) + ply = next(x for x in base["줄"] if x["cid"] == PLY) + assert more["기준"]["재료비"] > base["기준"]["재료비"] + ply["단가"] * 0.9 + out = _run(바탕={"로직": FORM}, 자재표시={PLY: {"별도계상": "별도계상"}}) + assert all(x["cid"] != PLY for x in out["줄"]) + assert out["기준"]["재료비"] < base["기준"]["재료비"] - ply["금액"] + 1 + + +def test_hopyo_line_keeps_line_default(): + whole = _whole() + line = {"로직": FORM, "입력값": {}, "패치": {"덮개": {PLY: {"qty": 2}}}} + got = labor._json(patch.run(whole, line)) + assert all(x["cid"] != PLY for x in got["줄"]) # 일위대가 기본 = 별도계상(222-1)