基于 skill-template 脚手架开发,包含: - payroll_config(费率配置,可编辑)+ payslip_snapshots(解析历史,只读)2 张业务表 - 全国默认 + 北上广 4 套费率(社保/公积金/个税起征点/7 级税率表) - 5 个业务 Action:calculate-payslip-simple / calculate-payslip-full / explain-term / view-config / edit-config - 个税累计预扣法计算,支持多月历史累加 - 20 个薪资名词库(社保/公积金/个税/全勤/加班费等) - 完整市场文档(README/TUTORIAL/DEMO/CHANGELOG) - 296 测试全部通过
148 lines
4.5 KiB
Python
148 lines
4.5 KiB
Python
"""费率配置读取服务。
|
||
|
||
从 payroll_config 表读取指定地区的费率;地区未单独插入时回退到全国默认。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import json
|
||
from typing import Any
|
||
|
||
from db.connection import get_conn
|
||
from util.logging_config import get_skill_logger
|
||
|
||
log = get_skill_logger()
|
||
|
||
|
||
def get_config_value(config_key: str, region: str = "default") -> float | str | None:
|
||
"""读取单个配置项的值。
|
||
|
||
优先取 (config_key, region);若该地区无记录,回退到 (config_key, 'default')。
|
||
"""
|
||
conn = get_conn()
|
||
try:
|
||
cur = conn.cursor()
|
||
cur.execute(
|
||
"SELECT config_value FROM payroll_config WHERE config_key=? AND region=?",
|
||
(config_key, region),
|
||
)
|
||
row = cur.fetchone()
|
||
if row is None and region != "default":
|
||
cur.execute(
|
||
"SELECT config_value FROM payroll_config WHERE config_key=? AND region='default'",
|
||
(config_key,),
|
||
)
|
||
row = cur.fetchone()
|
||
if row is None:
|
||
return None
|
||
return row[0]
|
||
finally:
|
||
conn.close()
|
||
|
||
|
||
def get_tax_brackets(region: str = "default") -> list[dict[str, Any]]:
|
||
"""读取 7 级累计预扣税率表。"""
|
||
raw = get_config_value("tax_brackets_json", region)
|
||
if raw is None or not isinstance(raw, str):
|
||
return []
|
||
try:
|
||
brackets = json.loads(raw)
|
||
return brackets if isinstance(brackets, list) else []
|
||
except (json.JSONDecodeError, TypeError):
|
||
return []
|
||
|
||
|
||
def get_region_config(region: str = "default") -> dict[str, Any]:
|
||
"""读取一个地区的全部费率配置,返回 dict。"""
|
||
keys = [
|
||
"social_pension_rate",
|
||
"social_medical_rate",
|
||
"social_unemployment_rate",
|
||
"social_base_min",
|
||
"social_base_max",
|
||
"housing_fund_rate",
|
||
"housing_fund_base_min",
|
||
"housing_fund_base_max",
|
||
"tax_threshold",
|
||
]
|
||
result: dict[str, Any] = {"region": region}
|
||
for key in keys:
|
||
result[key] = get_config_value(key, region)
|
||
result["tax_brackets"] = get_tax_brackets(region)
|
||
return result
|
||
|
||
|
||
def list_all_config(region: str | None = None) -> list[dict[str, Any]]:
|
||
"""列出费率配置(供 view-config Action)。
|
||
|
||
region 为 None 时列出全部地区;否则只列该地区(含回退默认值)。
|
||
"""
|
||
conn = get_conn()
|
||
try:
|
||
cur = conn.cursor()
|
||
if region is None:
|
||
cur.execute(
|
||
"SELECT config_key, region, config_value, display_name, description FROM payroll_config ORDER BY region, config_key"
|
||
)
|
||
else:
|
||
cur.execute(
|
||
"SELECT config_key, region, config_value, display_name, description FROM payroll_config WHERE region=? ORDER BY config_key",
|
||
(region,),
|
||
)
|
||
rows = cur.fetchall()
|
||
return [
|
||
{
|
||
"config_key": r[0],
|
||
"region": r[1],
|
||
"config_value": r[2],
|
||
"display_name": r[3],
|
||
"description": r[4],
|
||
}
|
||
for r in rows
|
||
]
|
||
finally:
|
||
conn.close()
|
||
|
||
|
||
def update_config_value(config_key: str, region: str, config_value: float) -> dict[str, Any]:
|
||
"""更新单个费率配置(供 edit-config Action)。
|
||
|
||
返回 {ok, skill, config_key, region, old_value, new_value}。
|
||
"""
|
||
log.info("config_update key=%s region=%s value=%s", config_key, region, config_value)
|
||
conn = get_conn()
|
||
try:
|
||
cur = conn.cursor()
|
||
cur.execute(
|
||
"SELECT config_value FROM payroll_config WHERE config_key=? AND region=?",
|
||
(config_key, region),
|
||
)
|
||
row = cur.fetchone()
|
||
if row is None:
|
||
return {
|
||
"ok": False,
|
||
"skill": "query-payslip-default",
|
||
"error_code": "CONFIG_NOT_FOUND",
|
||
"error": f"配置项 {config_key}(地区 {region})不存在",
|
||
}
|
||
old_value = row[0]
|
||
cur.execute(
|
||
"""
|
||
UPDATE payroll_config
|
||
SET config_value=?, updated_at=strftime('%s','now')
|
||
WHERE config_key=? AND region=?
|
||
""",
|
||
(config_value, config_key, region),
|
||
)
|
||
conn.commit()
|
||
return {
|
||
"ok": True,
|
||
"skill": "query-payslip-default",
|
||
"config_key": config_key,
|
||
"region": region,
|
||
"old_value": old_value,
|
||
"new_value": config_value,
|
||
}
|
||
finally:
|
||
conn.close()
|