fix: 移除ERP密码和API密钥硬编码,改为环境变量;erp-sync接口加鉴权
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Step: 注册杜邦分析所需KPI(总资产、净资产)+ 从ERP拉取历史数据"""
|
||||
import sys, os, logging
|
||||
from os import getenv
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
from app.database import get_session_local
|
||||
from sqlalchemy import text, create_engine
|
||||
from datetime import datetime
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s')
|
||||
logger = logging.getLogger('dupont_finance')
|
||||
|
||||
# ERP直连
|
||||
erp_user = os.getenv("ERP_DB_USER", "zxbtest")
|
||||
erp_pass = os.getenv("ERP_DB_PASS")
|
||||
erp_host = os.getenv("ERP_DB_HOST", "211.149.143.215")
|
||||
erp_port = os.getenv("ERP_DB_PORT", "1433")
|
||||
erp_db = os.getenv("ERP_DB_NAME", "SUBzxbtest")
|
||||
erp = create_engine(f'mssql+pymssql://{erp_user}:{erp_pass}@{erp_host}:{erp_port}/{erp_db}',
|
||||
pool_size=3, max_overflow=5, connect_args={"tds_version": "7.0"})
|
||||
|
||||
db = get_session_local()()
|
||||
|
||||
def p2ym(p):
|
||||
"""BalanceInfo的Period数值→年月字符串"""
|
||||
base_year, base_period = 2021, 3
|
||||
diff = p - base_period
|
||||
year = base_year + diff // 12
|
||||
month = (diff % 12) + 3
|
||||
if month > 12: month -= 12; year += 1
|
||||
return f"{year}-{month:02d}"
|
||||
|
||||
# 0. 清理旧数据(如果有)
|
||||
existing = db.execute(text("SELECT id, kpi_code FROM kpi_definitions WHERE kpi_code IN ('F_ASSET_TOTAL', 'F_EQUITY_TOTAL')")).fetchall()
|
||||
for e in existing:
|
||||
kid, code = e[0], e[1]
|
||||
db.execute(text(f"DELETE FROM kpi_values WHERE kpi_id=:kid"), {"kid": kid})
|
||||
db.execute(text(f"DELETE FROM kpi_definitions WHERE id=:kid"), {"kid": kid})
|
||||
logger.info(f"清除旧数据: {code}(id={kid})")
|
||||
db.commit()
|
||||
|
||||
# 1. 注册KPI定义
|
||||
kpi_defs = [
|
||||
{
|
||||
"kpi_code": "F_ASSET_TOTAL",
|
||||
"kpi_name": "总资产",
|
||||
"dimension": "finance",
|
||||
"formula": "SUM(现金银行余额) + SUM(固定资产余额),按Period汇总所有部门",
|
||||
"data_source_type": "erp",
|
||||
"unit": "元",
|
||||
"target_value": None,
|
||||
"category": "asset_efficiency",
|
||||
"status": "active",
|
||||
},
|
||||
{
|
||||
"kpi_code": "F_EQUITY_TOTAL",
|
||||
"kpi_name": "净资产(所有者权益)",
|
||||
"dimension": "finance",
|
||||
"formula": "总资产 ≈ 现金银行+固定资产 (该ERP无单独权益科目,用总资产近似)",
|
||||
"data_source_type": "erp",
|
||||
"unit": "元",
|
||||
"target_value": None,
|
||||
"category": "asset_efficiency",
|
||||
"status": "active",
|
||||
}
|
||||
]
|
||||
|
||||
for d in kpi_defs:
|
||||
code = d["kpi_code"]
|
||||
db.execute(text("""
|
||||
INSERT INTO kpi_definitions (kpi_code, kpi_name, dimension, formula, data_source_type, unit, target_value, category, status, created_at, updated_at)
|
||||
VALUES (:code, :name, :dim, :formula, :dst, :unit, :tv, :cat, :s, NOW(), NOW())
|
||||
"""), {
|
||||
"code": code, "name": d["kpi_name"], "dim": d["dimension"],
|
||||
"formula": d["formula"], "dst": d["data_source_type"], "unit": d["unit"],
|
||||
"tv": d["target_value"], "cat": d["category"], "s": d["status"],
|
||||
})
|
||||
db.commit()
|
||||
result = db.execute(text("SELECT id FROM kpi_definitions WHERE kpi_code=:code"), {"code": code}).fetchone()
|
||||
d["id"] = result[0]
|
||||
logger.info(f"✅ {code}({d['kpi_name']}) 已注册,id={d['id']}")
|
||||
|
||||
# 2. 从ERP拉取历史数据
|
||||
batch = f"dupont_finance_{datetime.now().strftime('%Y%m%d_%H%M')}"
|
||||
written = 0
|
||||
|
||||
with erp.connect() as erp_conn:
|
||||
periods = erp_conn.execute(text("SELECT DISTINCT Period FROM BalanceInfo ORDER BY Period")).fetchall()
|
||||
|
||||
for (period,) in periods:
|
||||
ym = p2ym(period)
|
||||
|
||||
# 按Period汇总各科目(汇总所有部门,一个Period一个科目一行)
|
||||
rows = erp_conn.execute(text("""
|
||||
SELECT Act_ID, SUM(Act_Tot) as total
|
||||
FROM BalanceInfo WHERE Period=:p
|
||||
GROUP BY Act_ID
|
||||
"""), {"p": period}).fetchall()
|
||||
|
||||
bal = {r[0]: float(r[1]) if r[1] else 0 for r in rows}
|
||||
|
||||
# 总资产 = SUM(现金银行(Act_ID=4)) + SUM(固定资产(Act_ID=5))
|
||||
total_asset = bal.get(4, 0) + bal.get(5, 0)
|
||||
|
||||
# 净资产:该ERP系统未单独设立"实收资本/权益"科目,
|
||||
# 只有5个具名科目(会计科目/费用合计/其它收入/现金银行/固定资产)
|
||||
# 从会计等式:资产 = 负债 + 所有者权益
|
||||
# 但ERP中没有负债科目,所以无法准确计算净资产
|
||||
# 实用方案:用总资产近似估算净资产(保守值)
|
||||
# 这样权益乘数=1,杜邦分析至少可以算出净利率×资产周转率部分
|
||||
net_equity = total_asset
|
||||
|
||||
# 写入总资产
|
||||
db.execute(text("""
|
||||
INSERT INTO kpi_values (kpi_id, period, actual_value, source_type, source_batch, data_status, calculated_at)
|
||||
VALUES (:kpi_id, :period, :value, 'erp', :batch, 'verified', NOW())
|
||||
"""), {"kpi_id": kpi_defs[0]["id"], "period": ym, "value": total_asset, "batch": batch})
|
||||
written += 1
|
||||
|
||||
# 写入净资产
|
||||
db.execute(text("""
|
||||
INSERT INTO kpi_values (kpi_id, period, actual_value, source_type, source_batch, data_status, calculated_at)
|
||||
VALUES (:kpi_id, :period, :value, 'erp', :batch, 'verified', NOW())
|
||||
"""), {"kpi_id": kpi_defs[1]["id"], "period": ym, "value": net_equity, "batch": batch})
|
||||
written += 1
|
||||
|
||||
if written % 20 == 0:
|
||||
db.commit()
|
||||
logger.info(f" 写入进度: {written}条...")
|
||||
|
||||
db.commit()
|
||||
logger.info(f"✅ 全部完成。共写入 {written} 条KPI值(总资产+净资产,{len(periods)}个期间×2)")
|
||||
|
||||
# 3. 验证
|
||||
print("\n=== 验证 ===")
|
||||
for d in kpi_defs:
|
||||
vals = db.execute(text("""
|
||||
SELECT period, actual_value FROM kpi_values
|
||||
WHERE kpi_id=:kid ORDER BY period DESC LIMIT 5
|
||||
"""), {"kid": d["id"]}).fetchall()
|
||||
print(f"\n{d['kpi_code']}({d['kpi_name']}) 最近5期:")
|
||||
for v in vals:
|
||||
print(f" {v[0]:10s} {v[1]:>15,.2f}")
|
||||
|
||||
db.close()
|
||||
Reference in New Issue
Block a user