""" CMA 财务示范数据 — 供财务BOT分析使用 运行: (cd /root/cma-management/backend && venv/bin/python3 scripts/seed_finance_data.py) """ import sys, os sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from app.database import get_session_local from app.models import KPIDefinition, KPIValue, KPIAlert, ActionPlan from app.models.budget_plan import BudgetPlan from datetime import datetime import logging logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s') logger = logging.getLogger("seed_finance") def seed(): db = get_session_local()() try: # ── 1. KPI字典 ── if db.query(KPIDefinition).count() == 0: kpis_data = [ # code, name, dim, cat, unit, target, freq, src, # g_min, y_min, r_min, dept, user ("F_REVENUE", "营业收入", "finance", "revenue_growth", "万元", 5000, "monthly", "erp", ">=4500", ">=4000", "<4000", "销售部", "张经理"), ("F_GROSS_MARGIN","毛利率", "finance", "profitability", "%", 35, "monthly", "erp", ">=35", ">=30", "<30", "财务部", "李会计"), ("F_NET_PROFIT", "净利润", "finance", "profitability", "万元", 800, "monthly", "erp", ">=800", ">=600", "<600", "财务部", ""), ("F_OP_CFLOW", "经营性现金流", "finance", "cash_risk", "万元", 1000, "monthly", "erp", ">=1000", ">=500", "<500", "财务部", ""), ("F_COST_RATIO", "费用率", "finance", "cost_control", "%", 20, "monthly", "erp", "<=20", "<=25", ">25", "财务部", ""), ("F_AR_DAYS", "应收账款周转天数","finance","asset_efficiency", "天", 45, "monthly", "erp", "<=45", "<=60", ">60", "销售部", ""), ("C_SATISFACTION","客户满意度", "customer","customer_satisfaction","分",92, "monthly","manual",">=92",">=85","<85","",""), ("C_NEW_CLIENTS", "新客户数", "customer","customer_scale", "个", 10, "monthly","business",">=10",">=5","<5","销售部",""), ("P_DELIVERY", "交付及时率", "process", "delivery_quality", "%", 95, "monthly", "erp", ">=95", ">=90", "<90", "交付部", ""), ("P_BUG_RATE", "缺陷率", "process", "delivery_quality", "%", 2, "monthly", "erp", "<=2", "<=5", ">5", "", ""), ("L_TRAINING", "培训完成率", "learning","talent_pipeline", "%", 90, "monthly","manual",">=90",">=80","<80","",""), ("L_EMPLOYEE_SAT","员工满意度", "learning","employee_engagement","分", 85, "quarterly","manual",None,None,None,"",""), ] formula_map = { "F_REVENUE": "SUM(erp_sales.amount)", "F_GROSS_MARGIN": "(收入-成本)/收入*100", "F_NET_PROFIT": "收入-成本-费用-税金", "F_COST_RATIO": "期间费用/收入*100", "F_AR_DAYS": "360/应收账款周转率", "P_BUG_RATE": "缺陷数/总功能点*100", } kpis = [] for code, name, dim, cat, unit, target, freq, src, g, y, r, dept, user in kpis_data: k = KPIDefinition( kpi_code=code, kpi_name=name, dimension=dim, category=cat, unit=unit, target_value=target, frequency=freq, data_source_type=src, threshold_green=g, threshold_yellow=y, threshold_red=r, responsible_dept=dept, responsible_user=user, status="active", ) if code in formula_map: k.formula = formula_map[code] db.add(k) kpis.append(k) db.flush() logger.info(f"✅ KPI字典: {len(kpis)}条") else: logger.info("KPI字典已有数据,跳过") # ── 2. KPI实际值(1-6月趋势)── if db.query(KPIValue).count() == 0: kpi_map = {k.kpi_code: k.id for k in db.query(KPIDefinition).all()} values_data = { "F_REVENUE": [4200, 4500, 4700, 4900, 5100, 4800], "F_GROSS_MARGIN":[32, 33, 34, 35, 34, 31], "F_NET_PROFIT": [650, 700, 750, 800, 820, 700], "F_OP_CFLOW": [800, 900, 950, 1000, 1050, 850], "F_COST_RATIO": [22, 21, 20, 19, 20, 23], "F_AR_DAYS": [50, 48, 46, 45, 44, 52], "C_SATISFACTION":[88, 89, 90, 91, 92, 90], "C_NEW_CLIENTS": [7, 8, 9, 10, 11, 8], "P_DELIVERY": [92, 93, 94, 95, 96, 93], "P_BUG_RATE": [3.0, 2.5, 2.0, 1.8, 1.5, 2.2], "L_TRAINING": [85, 87, 88, 90, 91, 86], } months = ["2026-01","2026-02","2026-03","2026-04","2026-05","2026-06"] cnt = 0 for code, vals in values_data.items(): kid = kpi_map.get(code) if not kid: continue for m, v in zip(months, vals): db.add(KPIValue(kpi_id=kid, period=m, actual_value=v, source_type="manual", data_status="verified")) cnt += 1 db.flush() logger.info(f"✅ KPI实际值: {cnt}条") else: logger.info("KPI实际值已有数据,跳过") # ── 3. 预警 ── if db.query(KPIAlert).count() == 0: kpi_map = {k.kpi_code: k.id for k in db.query(KPIDefinition).all()} alerts_data = [ ("F_REVENUE", "yellow", "6月营收4800万,低于目标5000万(缺口4%)"), ("F_GROSS_MARGIN", "red", "6月毛利率31%,跌破30%预警线"), ("F_NET_PROFIT", "yellow", "6月净利润700万,低于目标800万(-12.5%)"), ("F_OP_CFLOW", "yellow", "经营性现金流850万,低于目标1000万"), ("F_COST_RATIO", "yellow", "费用率23%,超目标20%"), ("F_AR_DAYS", "yellow", "应收账款周转52天,超目标45天"), ("C_NEW_CLIENTS", "yellow", "6月新客户仅8家,低于目标10家"), ] for code, level, msg in alerts_data: db.add(KPIAlert( kpi_id=kpi_map.get(code), alert_level=level, alert_message=msg, status="pending", )) db.flush() logger.info(f"✅ 预警: {len(alerts_data)}条") else: logger.info("预警已有数据,跳过") # ── 4. 改善行动 ── if db.query(ActionPlan).count() == 0: kpi_map = {k.kpi_code: k.id for k in db.query(KPIDefinition).all()} actions_data = [ (kpi_map.get("F_GROSS_MARGIN"), "成本优化专项 - 降低采购成本5%", "重新谈判供应商合同,目标毛利率回升至35%", "采购部王经理", "high", "in_progress", 40, datetime(2026,8,31)), (kpi_map.get("F_REVENUE"), "Q3客户拓展计划", "新增3个大客户,目标月度营收5500万", "销售部张经理", "high", "pending", 0, datetime(2026,9,30)), (kpi_map.get("F_AR_DAYS"), "应收账款催收行动", "集中催收超60天应收款,目标周转天降至45天", "财务部李会计", "medium", "in_progress", 30, datetime(2026,7,31)), (kpi_map.get("P_DELIVERY"), "交付流程优化", "优化项目管理流程,交付及时率提升至95%+", "交付部赵主管", "medium", "pending", 10, datetime(2026,8,15)), ] for kid, title, desc, assignee, pri, status, prog, due in actions_data: db.add(ActionPlan( kpi_id=kid, title=title, description=desc, assignee=assignee, priority=pri, status=status, progress=prog, due_date=due, )) db.flush() logger.info(f"✅ 改善行动: {len(actions_data)}条") else: logger.info("改善行动已有数据,跳过") # ── 5. 预算数据 ── if db.query(BudgetPlan).count() == 0: kpi_map = {k.kpi_code: k.id for k in db.query(KPIDefinition).all()} # 每个财务KPI全年12个月预算 budget_map = { "F_REVENUE": [3800,4000,4200,4400,4600,4800,4800,4900,5000,5000,5100,5200], "F_GROSS_MARGIN":[33]*12, "F_NET_PROFIT": [600,620,650,680,700,720,720,750,780,800,820,850], "F_OP_CFLOW": [700,750,800,850,900,950,950,1000,1000,1050,1050,1100], "F_COST_RATIO": [22]*12, "F_AR_DAYS": [50,49,48,47,46,45,45,44,44,43,43,42], } cnt = 0 for code, vals in budget_map.items(): kid = kpi_map.get(code) if not kid: continue for month in range(1, 13): db.add(BudgetPlan( kpi_id=kid, period=f"2026-{month:02d}", budget_value=vals[month-1], budget_year=2026, budget_month=month, version="v1.0", status="active", )) cnt += 1 db.flush() logger.info(f"✅ 预算数据: {cnt}条") else: logger.info("预算数据已有数据,跳过") db.commit() logger.info("") logger.info("🎉 财务示范数据导入完成!") logger.info(f"📊 KPI指标: {db.query(KPIDefinition).count()}") logger.info(f"📊 KPI实际值: {db.query(KPIValue).count()}") logger.info(f"📊 预警: {db.query(KPIAlert).count()}") logger.info(f"📊 改善行动: {db.query(ActionPlan).count()}") logger.info(f"📊 预算: {db.query(BudgetPlan).count()}") except Exception as e: db.rollback() logger.error(f"❌ 导入失败: {e}") import traceback; traceback.print_exc() raise finally: db.close() if __name__ == "__main__": seed()