From 3eeb7c3cc30168740f702a9acebbb9da6f80f23d Mon Sep 17 00:00:00 2001 From: umsangdon Date: Thu, 12 Nov 2026 18:57:42 +0900 Subject: [PATCH] =?UTF-8?q?fix(822-2b):=20=EB=82=B4=20=EA=B8=B0=EA=B3=84?= =?UTF-8?q?=20=EC=97=B0=EB=A3=8C=20+=20=EC=A0=84=EB=A0=A5=20=EB=91=98=20?= =?UTF-8?q?=EB=8B=A4=20=ED=97=88=EC=9A=A9=20=C2=B7=20=EC=83=88=20=EC=8B=9D?= =?UTF-8?q?=20=EB=91=98(GC001200=20=EC=97=B0=EB=A3=8C=20+=20=EC=A0=84?= =?UTF-8?q?=EB=A0=A5=20=C2=B7=20GC001201=20+=20=EC=A1=B0=EC=A2=85=EC=9B=90?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 내 기계 저장의 「주연료 · 전력은 하나만」 막음 뺌 — 불러오기 자동 생성(경쟁사 파워트로웰 = 손료 + 휘발유 + 잡품 + 전력)이 막히던 것 - 꼴 고름(logic_key) 연료 + 전력 → GC001200 · + 조종원(직종 있음) → GC001201 · 직종 없는 조종원 = 까닭 - 조종원 줄을 한 식에 넣지 않음 — 수식 계산기가 빈 직종 「LB:」 를 못 받음(전력 식 GC001196 · 1197 짝과 같은 꼴) - 수식 문서 두 줄 더함 · 802-2 대조 목록 다시 씀(두 줄 같음) · M01 정본 기계 · 다른 식 금액 그대로 - 낡은 시험: test_821_4 키대장 다음 번호 수 박은 기대 → 쓴 · 버린 번호보다 뒤(결번 재사용 없음) · test_822_2 연료 + 전력 None · 저장 400 기대 → 새 식 · 미리보기 = 서버 값 Co-Authored-By: Claude Opus 5.5 --- M02_MasterTemplete/M02_Machine_Hourly.py | 2 - M02_MasterTemplete/M02_MyMachine.py | 12 +- docs/raw/요구_1104/_목록_802-2_변환대조.xlsx | Bin 13302 -> 14384 bytes resources/master_data/_키대장.json | 4 +- .../로직_건설품셈_08장_건설기계.json | 108 ++++++++++++++++++ .../수식문서/수식_건설품셈_08장_건설기계.json | 86 ++++++++++++++ .../tester/test_821_4_import_my_machine.py | 4 +- .../tester/test_822_2_machine_hourly_all.py | 26 ++++- 8 files changed, 228 insertions(+), 14 deletions(-) diff --git a/M02_MasterTemplete/M02_Machine_Hourly.py b/M02_MasterTemplete/M02_Machine_Hourly.py index 0e4fb4952..f7b76b2cf 100644 --- a/M02_MasterTemplete/M02_Machine_Hourly.py +++ b/M02_MasterTemplete/M02_Machine_Hourly.py @@ -136,8 +136,6 @@ def why(row: dict, logic: dict | None) -> str: return f"주연료 「{vals['주연료']}」 — 수 아님" if float(vals.get("조종원") or 0) > 0 and not vals.get("조종원직종"): return "조종원직종 없음" - if vals.get("주연료") and vals.get("전력"): - return "주연료 · 전력 둘 다 — 식 없음" return "손료 계수 셋(상각 · 정비 · 관리) 없음" text = " ".join(str(x.get(k)) for k in ("식", "수량") for p in ("중간", "호표", "덧줄") for x in logic.get(p) or []) # fmt: skip diff --git a/M02_MasterTemplete/M02_MyMachine.py b/M02_MasterTemplete/M02_MyMachine.py index af0326b21..1187be642 100644 --- a/M02_MasterTemplete/M02_MyMachine.py +++ b/M02_MasterTemplete/M02_MyMachine.py @@ -12,6 +12,7 @@ 이 색인을 읽음 — 층을 모르는 길(`/api/m02/logic-lib/machine-hourly` 등)은 M01 만. 식 고름(채운 칸 · `logic_key` — M01 기계 목록 밖 줄도 같은 규칙 · 822-2) = 연료 + 조종원 GC000268(직종 없음 GC000275) · 연료만 GC000272(손료계수 한 칸 GC001198) · 조종원만 GC000276 · 전력 GC001196(+ 조종원 GC001197) · +연료 + 전력 GC001200(+ 조종원 GC001201 · 822-2b 경쟁사 기계) · 없음 GC000273 · 직접 GC001199(시간당 값 = 셈 꼴 값 칸 노무비 · 재료비 · 경비). 권한(본인 개인 층 · 맡은 회사 관리자)은 길(`M02_MyMachine_Router`) 몫. """ @@ -46,6 +47,7 @@ LOGIC = {(True, True): "GC000268", (True, False): "GC000272", (False, True): "GC000276", (False, False): "GC000273"} # fmt: skip JOB_IN, ONE_FUEL = "GC000275", "GC001198" # 직종 입력 · 손료계수 한 칸 연료 POWER, POWER_CREW, DIRECT_KEY = "GC001196", "GC001197", "GC001199" +FUEL_POWER, FUEL_POWER_CREW = "GC001200", "GC001201" # 연료 + 전력(+ 조종원) — 경쟁사 구성(822-2b) def mine(key: Any) -> bool: @@ -136,7 +138,7 @@ def _pos(v: Any) -> bool: def logic_key(row: dict) -> str | None: - """꼴(채운 칸) → 시간당 경비 식 키 · 못 고름(직종 없는 조종원만 · 연료 + 전력 · 수 아닌 연료 글) = None · + """꼴(채운 칸) → 시간당 경비 식 키 · 못 고름(직종 없는 조종원 · 수 아닌 연료 글) = None · 계수 셋(상각 · 정비 · 관리) 없음 = 손료계수 한 칸 식(연료만) · 그 밖은 그 식 + 모자란 칸 까닭.""" if row.get("방식") == DIRECT: return DIRECT_KEY @@ -144,8 +146,10 @@ def logic_key(row: dict) -> str | None: fuel, crew, power = (_pos(vals.get(k)) for k in ("주연료", "조종원", "전력")) job = bool(vals.get("조종원직종")) three = all(vals.get(k) is not None for k in COEFS) - if vals.get("주연료") not in (None, "", 0) and not fuel or fuel and power: + if vals.get("주연료") not in (None, "", 0) and not fuel: return None + if fuel and power: + return FUEL_POWER_CREW if crew and job else None if crew else FUEL_POWER if power: return POWER_CREW if crew and job else None if crew else POWER if fuel and not crew: @@ -181,7 +185,7 @@ def hourly(row: dict, index: Any) -> dict[str, Any]: mf = m01.cm.mf key = logic_key(row) if key is None: - return {"ok": False, **head, "reason": "식 없음 — 연료 · 전력은 하나만"} + return {"ok": False, **head, "reason": "식 없음 — 조종원직종 없음"} logic = index.logic(key) given = {i["이름"]: (i.get("고르기") or [""])[0] for i in logic.get("입력") or []} given["기계"] = str(row.get("원문번호")) @@ -239,8 +243,6 @@ def clean(raw: Any) -> dict[str, Any]: need = ("가격", "손료계수") if how == EASY else ("가격", *RATIOS) if lost := [k for k in need if not vals.get(k)]: raise _bad(f"빈 칸 — {' · '.join(lost)}") - if vals.get("주연료") and vals.get("전력"): - raise _bad("주연료 · 전력은 하나만(원문 운전경비 표 한 칸)") drop = (*RATIOS, *COEFS) if how == EASY else () vals = {k: v for k, v in vals.items() if k not in drop} if how == BOOK: diff --git a/docs/raw/요구_1104/_목록_802-2_변환대조.xlsx b/docs/raw/요구_1104/_목록_802-2_변환대조.xlsx index ada56c1562df2ae9a570f1ce1043a2cdd8c15b9f..49ffa570f78924a06d5011f67d9396dae4ba221d 100644 GIT binary patch delta 9063 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 None: assert "자체 발전기" not in text and "자체 콘크리트펌프용" not in text led = json.loads((ROOT / "resources/master_data/_키대장.json").read_text("utf-8")) assert all(k in led["폐기"] and k not in led["키"] for k in [*GONE, "GC001195"]) - assert led["다음"]["EQ"] == 669 and led["다음"]["GC"] == 1196 # 결번 · 재사용 안 함 + for pre in ("EQ", "GC"): # 결번 · 재사용 안 함 — 다음 번호가 쓴 · 버린 번호 모두보다 뒤 + used = [int(k[2:]) for k in [*led["키"], *led["폐기"]] if k.startswith(pre)] + assert led["다음"][pre] > max(used) alias = [r for r in eq.values() if r.get("별칭")] assert len(alias) >= 10 diff --git a/resources/tester/test_822_2_machine_hourly_all.py b/resources/tester/test_822_2_machine_hourly_all.py index 6194dbbbe..a728bafc2 100644 --- a/resources/tester/test_822_2_machine_hourly_all.py +++ b/resources/tester/test_822_2_machine_hourly_all.py @@ -4,7 +4,7 @@ ① 모든 기계 = 식 있음 또는 까닭 글(「셀 자료가 없음」 0) ② 식 고름 = 목록 › 꼴(채운 칸) 한 함수 ③ M01 [새 기계] 저장 = 꼴 식 목록 등록 · 지우면 목록에서 뺌 · 로직 파일 글 그대로(임시 마스터) ④ 직접 값 식(GC001199) — 내 기계 「직접」 시간당 · 줄 쌓기 식 ⑤ 전력 줄 — 점보드릴 135㎾ × 전력 단가 + -잡재료 6 % + 손료 + 조종원 · 계약종별 바꿈 · 내 기계 전력 ⑥ 옮긴 기계 견본(아스팔트 믹싱플랜트 · 타워크레인 · +잡재료 6 % + 손료 + 조종원 · 계약종별 바꿈 · 내 기계 전력 · 연료 + 전력(822-2b GC001200 · + 조종원 GC001201) ⑥ 옮긴 기계 견본(아스팔트 믹싱플랜트 · 타워크레인 · 직종 입력 = 아스팔트 스프레어) ⑦ 미리보기 = 서버 값 ⑧ 안 옮긴 기계 금액 그대로 ⑨ POWER TROWEL 이름. """ @@ -36,6 +36,7 @@ import master_machine_coef as mc # noqa: E402 MACHINE, LOGIC = "기계.json", "로직_건설품셈_08장_건설기계.json" POWER, POWER_CREW, ONE_FUEL, DIRECT = "GC001196", "GC001197", "GC001198", "GC001199" +FUEL_POWER, FUEL_POWER_CREW = "GC001200", "GC001201" NAME = "sub1임시기계822" # 안 옮긴 기계 — 822-2 전 금액(전국평균 · 기본 옵션) KEEP = {"EQ000001": 68550, "EQ000022": 108958} @@ -87,7 +88,10 @@ def test_pick_listed_then_shape(): assert shape({"값": {**full, "전력": 5, "조종원": 1, "조종원직종": "1048"}}) == POWER_CREW assert shape({"값": full}) == "GC000273" assert shape({"방식": "직접", "값": {}}) == DIRECT - assert shape({"값": {**full, "주연료": 1, "전력": 5}}) is None + assert shape({"값": {**full, "주연료": 1, "전력": 5}}) == FUEL_POWER + both = {**full, "주연료": 1, "전력": 5, "조종원": 1} + assert shape({"값": {**both, "조종원직종": "1048"}}) == FUEL_POWER_CREW + assert shape({"값": both}) is None # 직종 없는 조종원 — 까닭 assert shape({"값": {**full, "조종원": 1}}) is None # 직종 없는 조종원만 — 까닭 assert shape({"값": {**full, "주연료": "73.7+"}}) is None @@ -138,7 +142,9 @@ def test_new_machine_registers_and_unregisters(copy: Path): _save(client, [{"op": "delete", "key": key}]) assert "9999-0822" not in _codes("GC000268") assert (copy / LOGIC).read_text(encoding="utf-8") == logic_text - _save(client, [{"op": "add", "row": {**row, "원문번호": ""}}]) # 빈 원문번호 = 「자체 EQ…」 받을 키 + _save( + client, [{"op": "add", "row": {**row, "원문번호": ""}}] + ) # 빈 원문번호 = 「자체 EQ…」 받을 키 key = max(k for k, r in _rows().items() if r["이름"] == NAME) assert _rows()[key]["원문번호"] == f"자체 {key}" and f"자체 {key}" in _codes("GC000268") _save(client, [{"op": "delete", "key": key}]) @@ -213,8 +219,18 @@ def test_power_my_machine(world): assert hour["로직"] == POWER want = mc.preview({"가격": 1000, "손료계수": 2000, "전력": 10, "잡재료율": 5}, 0, 0, 114.4) assert (hour["재료비"], hour["경비"]) == (want["재료비"], want["경비"]) == (1201, 200) - with pytest.raises(store.StoreError): - _at(me, mym.create, {**body, "값": {**body["값"], "주연료": 1, "주연료종류": "경유"}}) + # 822-2b 연료 + 전력 둘 다(경쟁사 파워트로웰 꼴) — 손료 + 주연료 + 잡품(둘의 %) + 전력 = 미리보기 + vals = {**body["값"], "주연료": 1, "주연료종류": "휘발유"} + both = _at(me, mym.create, {**body, "규격": "연료전력", "값": vals})["시간당"] + oil = float(m01.loaded()[1].numbers["MO"]["휘발유:전국평균"]["값"]) + want = mc.preview(vals, oil, 0, 114.4) + assert both["로직"] == FUEL_POWER and want["주연료"] > 0 and want["전력"] > 0 + assert (both["재료비"], both["경비"], both["계"]) == (want["재료비"], want["경비"], want["계"]) + vals |= {"조종원": 1, "조종원직종": "1048"} # + 조종원 = GC001201 + crew = _at(me, mym.create, {**body, "규격": "연료전력조종원", "값": vals})["시간당"] + want = mc.preview(vals, oil, float(_wage("1048")), 114.4) + assert crew["로직"] == FUEL_POWER_CREW and crew["노무비"] == want["노무비"] > 0 + assert (crew["재료비"], crew["경비"], crew["계"]) == (want["재료비"], want["경비"], want["계"]) # ⑥ · ⑦ 옮긴 기계 견본 ─────────────────────────────────────────────────