# -*- coding: utf-8 -*- """PRJLST.SMT [n_KANJUB]·[KANJUB_ADD] 키 → 원가계산서(제잡비) 계산 설정 (kanjub_cfg.py). 설정 = 원본 저장키 그대로(keys · 차수 접두 뗌) + 다른 화면이 쓰는 옛 칸(rates·fixed·scheme·anjunAuto 등). 되돌려 쓰기 = smt_updates(cfg, slot) — 키 왕복(F2-58). 근거: 지식DB STmate/old/20_분석 12 §10(선택축) · 20 §2(KAN_ORD) · 25 §1(저장키 241종) · REA 18(D_ 고정 · 추가금액 키 꼴 ADD_AMT_g_ii_n) · REA 20(JAB_TYPE 계산 분류 · CAL_ACTL 실적). """ from __future__ import annotations # 원본 저장키 몸통 → 옛 항목코드(화면·다른 계산 모듈이 쓰는 이름) CODE_OF = {"T_KITA": "KITA", "EQIPSU": "EQIP"} KEY_OF = {v: k for k, v in CODE_OF.items()} REV_KEYS = ( "KANNO", "SANBO", "KOBO", "KUNBO", "SLVBO", "YUNBO", "TBUGUM", "ANJUN", "KITA", "HWAN", "EQIP", ) # 차수와 무관한 공통 키(JAB_TYPE 계산 분류 · CAL_ACTL 실적 · VIS_TOTL 총괄표시) GLOBAL_KEYS = ("JAB_TYPE", "CAL_ACTL", "VIS_TOTL") # 간접비 순서 코드표 기본값(Start8x.Ini · 20번 §2.1) KAN_ORD = { 1: ";301;302;303;310;304;305;306;312;313;321;307;308;309;311;361;362;363;364;365;366;367;368;369;370;", 2: ";351;301;302;303;310;304;305;306;312;313;331;332;333;334;335;336;337;338;307;308;309;311;361;362;363;364;365;366;367;368;369;370;", } def _num(kv: dict, key: str, default: float = 0.0) -> float: try: s = str(kv.get(key, "")).strip().replace(",", "") return float(s) if s else default except ValueError: return default def _val(v): """숫자로 읽히면 숫자, 아니면 글자 그대로.""" s = str(v).strip() try: f = float(s.replace(",", "")) return int(f) if f.is_integer() and "." not in s else f except ValueError: return s def add_rows(kv: dict) -> list[dict]: """추가금액 행 — ADD_CNT_g(묶음 1~4) · ADD_NAME/UNIT/BIGO_g_ii · ADD_AMT_g_ii_1~5(차수 칸) · ADD_BUGA(묶음2) · ADD_ADDT(묶음1).""" out = [] for g in range(1, 5): for i in range(1, int(_num(kv, f"ADD_CNT_{g}")) + 1): row = f"{g}_{i:02d}" out.append( { "group": g, "name": kv.get(f"ADD_NAME_{row}", ""), "unit": kv.get(f"ADD_UNIT_{row}", ""), "bigo": kv.get(f"ADD_BIGO_{row}", ""), "amts": [_num(kv, f"ADD_AMT_{row}_{s}") for s in range(1, 6)], "vat": _num(kv, f"ADD_BUGA_{row}") == 1, "addt": int(_num(kv, f"ADD_ADDT_{row}")), } ) return out def add_amounts(kv: dict, slot: int = 1) -> list[dict]: """옛 칸 — 차수 slot 금액만 뽑은 추가금액 목록(stage = 묶음 1~4).""" return [ {"stage": r["group"], "name": r["name"], "amount": r["amts"][slot - 1], "vat": r["vat"]} for r in add_rows(kv) ] def stage_slots(kv: dict) -> list[int]: """원가계산서가 저장된 차수 — [n_KANJUB] 묶음이 있는 차수.""" return [n for n in range(1, 5) if any(k.startswith(f"{n}_C_") for k in kv)] or [1] def scheme_of(keys: dict) -> str: """4형식 — JAB_TYPE 3(수자원)·4(지역난방) = 수공, CAL_ACTL=1 = 실적(REA 18·20 · 12번 §2).""" su = int(_num(keys, "JAB_TYPE")) in (3, 4) actl = int(_num(keys, "CAL_ACTL")) == 1 return ("SA" if su else "TA") if actl else ("S" if su else "T") def build_kanjub_config(kv: dict, slot: int = 1) -> dict: p = f"{slot}_" keys = {k[len(p) :]: _val(v) for k, v in kv.items() if k.startswith(p)} keys.update({k: _val(kv[k]) for k in GLOBAL_KEYS if k in kv}) rates = { CODE_OF.get(k[2:], k[2:]): float(_val(v) or 0) for k, v in keys.items() if k.startswith("R_") and isinstance(_val(v), (int, float)) } fixed = { CODE_OF.get(k[2:], k[2:]): _num(keys, "C_" + k[2:]) for k in keys if k.startswith("D_") and _num(keys, k) == 1 } return { "slot": slot, "scheme": scheme_of(keys), "keys": keys, "rates": rates, "fixed": fixed, "revModes": {k: "recalculated" for k in REV_KEYS}, "anjunAuto": int(_num(keys, "I_ANJUNAUTO")), "anjunBaseAdd": _num(keys, "C_ANJUN_G"), "addRows": add_rows(kv), "addAmounts": add_amounts(kv, slot), "kanOrd": dict(KAN_ORD), } def smt_updates(cfg: dict, slot: int | None = None, keys_out: dict | None = None) -> dict: """설정(+계산 결과 키) → PRJLST.SMT 갱신 사전(n_KEY · 공통 키 · ADD_*) — 저장 때 server.save 의 smt_updates 로 넘김.""" n = slot or int(cfg.get("slot", 1)) keys = dict(cfg.get("keys") or {}) for code, r in (cfg.get("rates") or {}).items(): keys["R_" + KEY_OF.get(code, code)] = r if cfg.get("anjunAuto") is not None: keys["I_ANJUNAUTO"] = cfg["anjunAuto"] keys.update(keys_out or {}) out = {} for k, v in keys.items(): out[k if k in GLOBAL_KEYS else f"{n}_{k}"] = _fmt(v) rows = cfg.get("addRows") or [] for g in range(1, 5): mine = [r for r in rows if int(r.get("group", 0)) == g] out[f"ADD_CNT_{g}"] = str(len(mine)) for i, r in enumerate(mine, 1): row = f"{g}_{i:02d}" out[f"ADD_NAME_{row}"] = r.get("name", "") out[f"ADD_UNIT_{row}"] = r.get("unit", "") out[f"ADD_BIGO_{row}"] = r.get("bigo", "") for s, a in enumerate((list(r.get("amts") or []) + [0] * 5)[:5], 1): out[f"ADD_AMT_{row}_{s}"] = _fmt(a) if g == 2: out[f"ADD_BUGA_{row}"] = "1" if r.get("vat") else "0" if g == 1: out[f"ADD_ADDT_{row}"] = str(int(r.get("addt") or 0)) return out def _fmt(v) -> str: if isinstance(v, bool): return "1" if v else "0" if isinstance(v, float) and v.is_integer(): return str(int(v)) return str(v)