146 lines
5.8 KiB
Python
146 lines
5.8 KiB
Python
#!/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()
|