Files
Aislo/tools/stmate-test/kanjub_cfg.py
T

160 lines
5.9 KiB
Python

# -*- 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)