"""管理会计OS 数据模型""" from sqlalchemy import Column, Integer, String, Text, Float, DateTime, ForeignKey, Boolean, JSON, func, UniqueConstraint, Numeric, Date from app.database import Base from app.models.budget_plan import BudgetPlan from app.models.cost_model import StandardCost, ActualCost, AbcActivity, AbcAllocation from app.models.knowledge import KnowledgeEvent, KnowledgeSummary from app.models.driver_budget import DriverFactorTemplate, DriverFactorBudget class Entity(Base): """企业实体""" __tablename__ = "entities" id = Column(Integer, primary_key=True, index=True) name = Column(String(100), nullable=False, comment="企业全称") short_name = Column(String(50), comment="企业简称") industry = Column(String(50), comment="行业") status = Column(String(20), default="active", comment="active/inactive/demo") created_at = Column(DateTime, server_default=func.now()) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) class User(Base): """用户""" __tablename__ = "users" id = Column(Integer, primary_key=True, index=True) username = Column(String(50), unique=True, nullable=False) password_hash = Column(String(128), nullable=False) name = Column(String(100), nullable=False) role = Column(String(20), default="finance") # ceo / finance / business / it phone = Column(String(20), nullable=True) created_at = Column(DateTime, server_default=func.now()) class UserEntity(Base): """用户-企业授权(账套模式多对多)— 新表必须带entity_id(开发规范)""" __tablename__ = "user_entities" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True, comment="用户ID") entity_id = Column(Integer, ForeignKey("entities.id"), nullable=False, index=True, comment="企业ID(账套)") granted_by = Column(Integer, nullable=True, comment="授权人") created_at = Column(DateTime, server_default=func.now()) __table_args__ = (UniqueConstraint("user_id", "entity_id", name="uq_user_entity"),) class StrategicMap(Base): """战略地图""" __tablename__ = "strategic_maps" id = Column(Integer, primary_key=True, index=True) title = Column(String(200), nullable=False, comment="地图名称") version = Column(String(20), default="v1.0", comment="版本号") status = Column(String(20), default="draft", comment="draft/published") dimensions = Column(JSON, nullable=True, comment="四维度和目标列表") canvas_data = Column(JSON, nullable=True, comment="画布连线数据") created_by = Column(Integer, nullable=True) created_at = Column(DateTime, server_default=func.now()) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) class KPIDefinition(Base): """KPI字典""" __tablename__ = "kpi_definitions" id = Column(Integer, primary_key=True, index=True) entity_id = Column(Integer, default=1, comment="企业ID") map_id = Column(Integer, ForeignKey("strategic_maps.id"), nullable=True, comment="关联战略地图") kpi_code = Column(String(50), unique=True, nullable=False, comment="KPI编码") kpi_name = Column(String(200), nullable=False, comment="KPI名称") dimension = Column(String(50), comment="所属维度: finance/customer/process/learning") objective = Column(String(200), comment="关联战略目标") formula = Column(Text, nullable=True, comment="计算公式") formula_desc = Column(String(500), nullable=True, comment="公式说明") data_source_type = Column(String(20), default="manual", comment="erp/business/excel/manual") data_source_config = Column(JSON, nullable=True, comment="数据源配置") data_source = Column(String(500), default="待补充", comment="数据来源") data_owner = Column(String(100), default="待指定", comment="数据责任人") frequency = Column(String(20), default="monthly", comment="daily/weekly/monthly/quarterly/yearly") unit = Column(String(50), default="%", comment="单位") target_value = Column(Float, nullable=True, comment="目标值(兼容旧字段)") target_monthly = Column(Float, nullable=True, comment="月度目标值") target_quarterly = Column(Float, nullable=True, comment="季度目标值") target_yearly = Column(Float, nullable=True, comment="年度目标值") target_calc_type = Column(String(20), nullable=True, comment="指标类型: accumulate累计(月×3=季,×12=年) / ratio比率(季/年沿用基准,可手调)") threshold_green = Column(String(100), nullable=True, comment="绿灯阈值") threshold_yellow = Column(String(100), nullable=True, comment="黄灯阈值") threshold_red = Column(String(100), nullable=True, comment="红灯阈值") category = Column(String(50), nullable=True, comment="BSC二级类别: revenue_growth/profitability/cost_control/asset_efficiency/cash_risk/customer_scale/customer_concentration/customer_satisfaction/supply_chain/delivery_quality/talent_pipeline/employee_engagement/innovation") responsible_dept = Column(String(200), nullable=True, comment="负责部门") responsible_user = Column(String(100), nullable=True, comment="负责人") kpi_level = Column(String(20), default="operational", comment="strategic/operational") status = Column(String(20), default="active") bot_source = Column(String(50), nullable=True, comment="Bot标识: finance-bot/ops-bot等") epic = Column(String(50), default="Epic2", comment="所属Epic") created_by = Column(Integer, nullable=True) created_at = Column(DateTime, server_default=func.now()) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) class KPIValue(Base): """KPI实际值""" __tablename__ = "kpi_values" id = Column(Integer, primary_key=True, index=True) entity_id = Column(Integer, default=None, comment="企业ID (P2多租户隔离 2026-08-23, 按kpi_id回填)") kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False) period = Column(String(20), nullable=False, comment="期间 2026-05") actual_value = Column(Float, nullable=True, comment="实际值") source_type = Column(String(20), default="manual", comment="erp/excel/manual") source_batch = Column(String(100), nullable=True, comment="导入批次号") data_status = Column(String(20), default="pending", comment="pending/verified/error") calculated_at = Column(DateTime, server_default=func.now()) remark = Column(String(500), nullable=True) class DataSourceConfig(Base): """数据源配置""" __tablename__ = "data_source_config" id = Column(Integer, primary_key=True, index=True) name = Column(String(200), nullable=False, comment="数据源名称") source_type = Column(String(20), nullable=False, comment="erp/business/excel") api_endpoint = Column(String(500), nullable=True, comment="API地址") api_key = Column(String(200), nullable=True, comment="API Key") query_sql = Column(Text, nullable=True, comment="SQL查询语句") sync_type = Column(String(20), default="realtime", comment="realtime/batch") status = Column(String(20), default="active") last_sync_at = Column(DateTime, nullable=True) created_at = Column(DateTime, server_default=func.now()) class KPIAlert(Base): """预警记录""" __tablename__ = "kpi_alerts" id = Column(Integer, primary_key=True, index=True) kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False) kpi_value_id = Column(Integer, ForeignKey("kpi_values.id"), nullable=True) alert_level = Column(String(20), default="yellow", comment="green/yellow/red") alert_message = Column(String(500), nullable=False) alert_type = Column(String(30), default="actual", comment="actual/forecast — 实际值超限/预测值超限") suggestion = Column(Text, nullable=True, comment="情景建议") action_plan_linked_id = Column(Integer, nullable=True, comment="关联的改善计划ID") status = Column(String(20), default="pending", comment="pending/processing/resolved") assignee = Column(String(100), nullable=True, comment="处理人") resolution = Column(Text, nullable=True, comment="处理结果") resolved_at = Column(DateTime, nullable=True) created_at = Column(DateTime, server_default=func.now()) class OperationLog(Base): """操作日志""" __tablename__ = "operation_logs" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, nullable=True) action = Column(String(50), nullable=False, comment="create/update/delete/calculate/import") target_type = Column(String(50), nullable=False, comment="kpi/map/alert/source") target_id = Column(Integer, nullable=True) detail = Column(JSON, nullable=True) created_at = Column(DateTime, server_default=func.now()) class NotificationChannel(Base): """通知渠道配置""" __tablename__ = "notification_channels" id = Column(Integer, primary_key=True, index=True) name = Column(String(100), nullable=False, comment="渠道名称") channel_type = Column(String(30), nullable=False, comment="wecom/mail/sms") config = Column(JSON, nullable=True, comment="渠道配置") enabled = Column(Boolean, default=True) created_at = Column(DateTime, server_default=func.now()) class NotificationLog(Base): """通知发送日志""" __tablename__ = "notification_logs" id = Column(Integer, primary_key=True, index=True) alert_id = Column(Integer, ForeignKey("kpi_alerts.id"), nullable=True) channel = Column(String(30), nullable=False, comment="wecom/mail") recipient = Column(String(200), nullable=True, comment="收件人") title = Column(String(200), nullable=True) content = Column(Text, nullable=True) status = Column(String(20), default="pending", comment="pending/sent/failed") error_msg = Column(String(500), nullable=True) sent_at = Column(DateTime, nullable=True) created_at = Column(DateTime, server_default=func.now()) class RolePermission(Base): """角色权限配置(单条记录,key-value)""" __tablename__ = "role_permissions" id = Column(Integer, primary_key=True, index=True) key = Column(String(50), unique=True, nullable=False, comment="配置键: route_permissions / action_permissions") value = Column(JSON, nullable=False, comment="配置值") updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) class Objective(Base): """OKR目标""" __tablename__ = "objectives" id = Column(Integer, primary_key=True, index=True) entity_id = Column(Integer, default=1, comment="企业ID (P2多租户隔离 2026-08-23)") title = Column(String(200), nullable=False, comment="目标标题") description = Column(Text, nullable=True, comment="目标描述") dimension = Column(String(50), nullable=True, comment="关联维度: finance/customer/process/learning") strategic_map_id = Column(Integer, ForeignKey("strategic_maps.id"), nullable=True, comment="关联战略地图") quarter = Column(String(20), nullable=False, comment="季度: 2026Q3") owner = Column(String(100), nullable=True, comment="负责人") status = Column(String(20), default="active", comment="active/completed/cancelled") progress = Column(Integer, default=0, comment="整体进度 0-100") confidence = Column(Integer, default=5, comment="信心指数 1-10") created_at = Column(DateTime, server_default=func.now()) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) class ActionPlan(Base): """改善行动计划""" __tablename__ = "action_plans" id = Column(Integer, primary_key=True, index=True) alert_id = Column(Integer, ForeignKey("kpi_alerts.id"), nullable=True, comment="关联预警") kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="关联KPI") objective_id = Column(Integer, ForeignKey("objectives.id"), nullable=True, comment="关联OKR目标") title = Column(String(200), nullable=False, comment="计划标题") description = Column(Text, nullable=True, comment="详细描述") assignee = Column(String(100), nullable=True, comment="负责人") priority = Column(String(20), default="medium", comment="high/medium/low") due_date = Column(DateTime, nullable=True, comment="截止日期") status = Column(String(20), default="pending", comment="pending/in_progress/completed/cancelled") progress = Column(Integer, default=0, comment="完成进度 0-100") result = Column(Text, nullable=True, comment="改善结果") monthly_milestones = Column(JSON, nullable=True, comment="月度里程碑: [{\"month\":\"2026-07\",\"label\":\"...\",\"status\":\"completed\"}]") auto_verify_rule = Column(JSON, nullable=True, comment="自动验证规则: {\"condition\": \"value > target\"}") verify_result = Column(String(20), nullable=True, comment="验证结果: pass/fail/pending") verify_log = Column(JSON, nullable=True, comment="验证历史日志") verify_status = Column(String(20), default="pending", comment="验证状态: pending/passed/failed/retrying/escalated") verify_attempts = Column(Integer, default=0, comment="验证尝试次数") verified_at = Column(DateTime, nullable=True, comment="验证完成时间") kpi_current_before = Column(Float, nullable=True, comment="执行前KPI值") kpi_current_after = Column(Float, nullable=True, comment="执行后KPI值") created_by = Column(String(100), nullable=True, comment="创建人") created_at = Column(DateTime, server_default=func.now()) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) class OrgNode(Base): """组织节点: 集团→事业部→区域→部门→班组 5级""" __tablename__ = "org_nodes" id = Column(Integer, primary_key=True, index=True) parent_id = Column(Integer, ForeignKey("org_nodes.id"), nullable=True, comment="父节点ID") name = Column(String(100), nullable=False, comment="节点名称") code = Column(String(50), unique=True, nullable=True, comment="编码") level = Column(Integer, nullable=False, comment="1=集团 2=事业部 3=区域 4=部门 5=班组") sort_order = Column(Integer, default=0, comment="排序") enabled = Column(Integer, default=1, comment="1启用 0禁用") path = Column(String(500), nullable=True, comment="路径") remark = Column(String(200), nullable=True, comment="备注") created_at = Column(DateTime, server_default=func.now()) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) class StrategicMapVersion(Base): """战略地图版本快照""" __tablename__ = "strategic_map_versions" id = Column(Integer, primary_key=True, index=True) map_id = Column(Integer, ForeignKey("strategic_maps.id", ondelete="CASCADE"), nullable=False, comment="关联地图") version = Column(String(20), nullable=False, comment="版本号 v1.0 v1.1 ...") dimensions = Column(JSON, nullable=False, comment="维度数据快照") canvas_data = Column(JSON, nullable=False, comment="画布数据快照") comment = Column(String(500), nullable=True, comment="说明") created_by = Column(Integer, nullable=True) created_at = Column(DateTime, server_default=func.now()) class MapObjective(Base): """战略地图目标: 每个维度下的具体目标""" __tablename__ = "map_objectives" id = Column(Integer, primary_key=True, index=True) map_id = Column(Integer, ForeignKey("strategic_maps.id", ondelete="CASCADE"), nullable=False, comment="关联地图") dimension_key = Column(String(50), nullable=False, comment="所属维度: finance/customer/process/learning") name = Column(String(200), nullable=False, comment="目标名称") description = Column(Text, nullable=True, comment="描述") icon = Column(String(50), default="target", comment="图标标识") kpis = Column(JSON, nullable=True, comment="关联KPI编码数组") sort_order = Column(Integer, default=0, comment="排序") created_at = Column(DateTime, server_default=func.now()) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) class KPICausality(Base): """KPI因果链 — 记录KPI间的因果关系""" __tablename__ = "kpi_causality" id = Column(Integer, primary_key=True, index=True) source_kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="源KPI(因)") target_kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="目标KPI(果)") strength = Column(Float, default=0.5, comment="影响强度 0~1") lag_months = Column(Integer, default=1, comment="滞后期(月)") formula = Column(String(500), nullable=True, comment="影响公式描述") direction = Column(String(10), default="positive", comment="positive/negative 正向/负向影响") created_at = Column(DateTime, server_default=func.now()) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) class MpmResult(Base): """MPM财务Bot分析结果记录""" __tablename__ = "mpm_results" id = Column(Integer, primary_key=True, index=True) entity_id = Column(Integer, ForeignKey("entities.id"), nullable=False, comment="企业实体ID") period = Column(String(20), nullable=False, comment="期间 YYYY-MM") source = Column(String(50), default="finance-bot", comment="来源Bot标识") raw_data = Column(JSON, nullable=False, comment="完整的MPM计算结果") created_at = Column(DateTime, server_default=func.now()) class BotBridgeConfig(Base): """Bot桥接鉴权配置""" __tablename__ = "bot_bridge_config" id = Column(Integer, primary_key=True, index=True) bot_name = Column(String(50), unique=True, nullable=False, comment="Bot名称") token = Column(String(64), nullable=False, comment="鉴权Token") is_active = Column(Boolean, default=True, comment="是否激活") created_at = Column(DateTime, server_default=func.now()) class KpiDataQualityLog(Base): """数据质量监控日志""" __tablename__ = "kpi_data_quality_log" id = Column(Integer, primary_key=True, index=True) kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="关联KPI") check_type = Column(String(30), nullable=False, comment="abnormal_change/flat_data/missing_data/value_outlier") severity = Column(String(20), default="warning", comment="info/warning/critical") detail = Column(JSON, nullable=True, comment="检测详情") suggestion = Column(String(500), nullable=True, comment="建议操作") status = Column(String(20), default="open", comment="open/resolved/ignored") resolved_at = Column(DateTime, nullable=True) created_at = Column(DateTime, server_default=func.now()) class BiReportTemplate(Base): """BI报表模板""" __tablename__ = "bi_report_templates" id = Column(Integer, primary_key=True, index=True) name = Column(String(200), nullable=False, comment="模板名称") report_type = Column(String(50), nullable=False, comment="overview/trend/comparison/topn/causality") config = Column(JSON, nullable=False, comment="报表配置") is_system = Column(Integer, default=0, comment="系统预置模板") created_by = Column(Integer, nullable=True) created_at = Column(DateTime, server_default=func.now()) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) class BiReport(Base): """用户保存的BI报表""" __tablename__ = "bi_reports" id = Column(Integer, primary_key=True, index=True) template_id = Column(Integer, ForeignKey("bi_report_templates.id"), nullable=True) name = Column(String(200), nullable=False, comment="报表名称") config = Column(JSON, nullable=False, comment="报表配置(行/列/值)") chart_type = Column(String(50), default="auto", comment="图表类型") is_shared = Column(Integer, default=0, comment="是否分享") created_by = Column(Integer, nullable=True) created_at = Column(DateTime, server_default=func.now()) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) class BscLayerConfig(Base): """BSC四层配置 — 不同企业的权重配置""" __tablename__ = "bsc_layer_config" id = Column(Integer, primary_key=True, index=True) entity_id = Column(Integer, ForeignKey("entities.id"), nullable=False, comment="企业ID") layer = Column(String(20), nullable=False, comment="financial/customer/process/learning") weight = Column(Float, nullable=False, comment="该层权重(%)") kpi_count_min = Column(Integer, default=2, comment="最少KPI数") kpi_count_max = Column(Integer, default=5, comment="最多KPI数") class KPIHierarchy(Base): """KPI层级关系 — 公司→部门→个人三级分解""" __tablename__ = "kpi_hierarchy" id = Column(Integer, primary_key=True, index=True) entity_id = Column(Integer, ForeignKey("entities.id"), default=1, comment="企业ID") parent_kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="上级KPI") child_kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="下级KPI") level = Column(Integer, default=1, comment="1=公司级 2=部门级 3=个人级") weight = Column(Float, default=1.0, comment="下级对上级的贡献权重(%)") child_name = Column(String(200), nullable=True, comment="下级节点名称(个人或部门名)") created_at = Column(DateTime, server_default=func.now()) # 兼容性: P2开发新增的模板API需要的模型 # KPI模板(独立表) class KPITemplate(Base): __tablename__ = "kpi_templates" id = Column(Integer, primary_key=True, index=True) kpi_code = Column(String(50), unique=True, nullable=False) kpi_name = Column(String(200), nullable=False) dimension = Column(String(50)) category = Column(String(50)) formula = Column(Text) formula_desc = Column(String(500)) unit = Column(String(50)) target_value = Column(Float) description = Column(Text) is_system = Column(Integer, default=0) usage_count = Column(Integer, default=0) created_by = Column(Integer) created_at = Column(DateTime, server_default=func.now()) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) class OKRTemplate(Base): """OKR模板库""" __tablename__ = "okr_templates" id = Column(Integer, primary_key=True, index=True) name = Column(String(100), nullable=False, comment="O名称") description = Column(Text, comment="O描述") dimension = Column(String(20), nullable=False, comment="finance/customer/process/learning") layer = Column(String(20), default="level1", comment="level1/level2/level3") industry_tag = Column(String(50), default="general", comment="行业标签") preset_krs = Column(JSON, nullable=False, comment="预设关键结果列表") source = Column(String(20), default="system", comment="system/user/industry_pack") owner = Column(String(50), default="CMA标准库", comment="模板负责人(治理P2: 可追踪)") use_count = Column(Integer, default=0, comment="使用次数") sort_order = Column(Integer, default=0) is_active = Column(Integer, default=1) created_at = Column(DateTime, server_default=func.now()) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) class CashForecast(Base): """现金流预测 — 每日未来30天预测""" __tablename__ = "cash_forecasts" id = Column(Integer, primary_key=True, index=True) entity_id = Column(Integer, ForeignKey("entities.id"), nullable=False, comment="企业ID") forecast_date = Column(DateTime, nullable=False, comment="预测日期(每天一条)") predicted_cash = Column(Float, nullable=True, comment="预测现金余额") lower_bound = Column(Float, nullable=True, comment="置信区间下界") upper_bound = Column(Float, nullable=True, comment="置信区间上界") alert_status = Column(String(20), default="green", comment="green/yellow/red") created_at = Column(DateTime, server_default=func.now()) class CashPlan(Base): """收付款计划 — 资金管理智能体(唯一应收载体:含回款登记、负责人、数据来源)""" __tablename__ = "cash_plans" id = Column(Integer, primary_key=True, index=True) entity_id = Column(Integer, ForeignKey("entities.id"), default=1, comment="企业ID") plan_type = Column(String(10), nullable=False, comment="receive收/pay付") amount = Column(Float, nullable=False, comment="金额(万元)") plan_date = Column(DateTime, nullable=False, comment="计划日期(应收即到期日)") counterparty = Column(String(200), nullable=True, comment="关联客户/供应商") description = Column(String(500), nullable=True, comment="说明") status = Column(String(20), default="pending", comment="pending/completed/cancelled") owner = Column(String(100), nullable=True, comment="负责人/业务员(应收催收责任人)") source = Column(String(50), default="manual", comment="数据来源: manual/bohai_ar/receivables_migrate") paid_amount = Column(Float, default=0, comment="已回款金额(万元)") completed_at = Column(DateTime, nullable=True, comment="完成时间") created_at = Column(DateTime, server_default=func.now()) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) class SystemConfig(Base): """系统配置 — key-value存储""" __tablename__ = "system_configs" id = Column(Integer, primary_key=True, index=True) config_key = Column(String(100), unique=True, nullable=False, comment="配置键") config_value = Column(String(500), nullable=True, comment="配置值") description = Column(String(500), nullable=True, comment="配置说明") updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) class BudgetDeviationAlert(Base): """预算偏差预警记录""" __tablename__ = "budget_deviation_alerts" id = Column(Integer, primary_key=True, index=True) kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="关联KPI") period = Column(String(20), nullable=False, comment="期间 YYYY-MM") budget_value = Column(Float, nullable=True, comment="预算值") actual_value = Column(Float, nullable=True, comment="实际值") deviation_rate = Column(Float, nullable=True, comment="偏差率 %") deviation_value = Column(Float, nullable=True, comment="偏差绝对值") alert_level = Column(String(20), default="warning", comment="warning/critical") status = Column(String(20), default="open", comment="open/resolved/ignored") suggestion = Column(String(500), nullable=True, comment="处理建议") created_at = Column(DateTime, server_default=func.now()) class ReportHistory(Base): """自动生成的经营分析报告记录""" __tablename__ = "report_history" id = Column(Integer, primary_key=True, index=True) report_type = Column(String(20), nullable=False, comment="weekly/monthly/special") period = Column(String(20), nullable=False, comment="期间: 2026-W30 / 2026-07 / 2026-Q2") title = Column(String(200), nullable=False, comment="报告标题") markdown_content = Column(Text, nullable=True, comment="Markdown格式报告(用于微信推送)") json_content = Column(JSON, nullable=True, comment="JSON结构化数据(写入CMA系统)") status = Column(String(20), default="generated", comment="generated/pushed/failed") trigger_type = Column(String(20), default="manual", comment="manual/scheduled/event") alert_ref = Column(String(50), nullable=True, comment="事件触发时的预警引用") created_at = Column(DateTime, server_default=func.now()) class AnalysisResult(Base): """财务Bot分析结论 — 带置信度评分""" __tablename__ = "analysis_results" id = Column(Integer, primary_key=True, index=True) period = Column(String(20), nullable=False, comment="期间 YYYY-MM") conclusion = Column(String(1000), nullable=False, comment="分析结论") confidence = Column(Integer, nullable=False, comment="置信度 0-100") data_source = Column(String(500), nullable=True, comment="数据来源") calculation_logic = Column(String(1000), nullable=True, comment="计算逻辑") comparable_benchmark = Column(String(500), nullable=True, comment="可比基准") limitations = Column(String(1000), nullable=True, comment="局限说明") has_actual = Column(Integer, default=0, comment="有实际值") has_target = Column(Integer, default=0, comment="有目标值") has_trend = Column(Integer, default=0, comment="有历史趋势") has_review = Column(Integer, default=0, comment="有人工复核") kpi_code = Column(String(50), nullable=True, comment="关联KPI编码") kpi_name = Column(String(200), nullable=True, comment="关联KPI名称") created_at = Column(DateTime, server_default=func.now()) class ForecastAccuracy(Base): """预测准确率 — 上期预测 vs 本期实际""" __tablename__ = "forecast_accuracy" id = Column(Integer, primary_key=True, index=True) entity_id = Column(Integer, ForeignKey("entities.id"), nullable=False, comment="企业ID") period = Column(String(20), nullable=False, comment="期间 2026-07") forecast_value = Column(Float, nullable=True, comment="预测值") actual_value = Column(Float, nullable=True, comment="实际值") mae = Column(Float, nullable=True, comment="绝对误差") mape = Column(Float, nullable=True, comment="百分比误差") created_at = Column(DateTime, server_default=func.now()) class ScenarioSuggestion(Base): """情景建议模板 — 根据不同预警类型自动生成建议""" __tablename__ = "scenario_suggestions" id = Column(Integer, primary_key=True, index=True) alert_type = Column(String(30), nullable=False, comment="预警类型: cash_low/cash_critical/cost_high/revenue_drop") title = Column(String(200), nullable=False, comment="建议标题") description = Column(Text, nullable=True, comment="详细建议") action_template = Column(Text, nullable=True, comment="改善行动模板") priority = Column(String(20), default="medium", comment="high/medium/low") sort_order = Column(Integer, default=0, comment="排序") created_at = Column(DateTime, server_default=func.now()) # ============================================================ # 新30号准则模型 (2027) # ============================================================ class Subject(Base): """会计科目 — 新30号准则分类""" __tablename__ = "subjects" id = Column(Integer, primary_key=True, index=True) subject_code = Column(String(20), nullable=False, unique=True, comment="科目编码") subject_name = Column(String(200), nullable=False, comment="科目名称") parent_code = Column(String(20), nullable=True, comment="上级科目编码") level = Column(Integer, default=1, comment="科目级别 1-4") category = Column(String(50), nullable=True, comment="科目类别") new_standard_category = Column(String(20), nullable=True, comment="新30号准则分类: operating/investing/financing/tax/discontinued") is_active = Column(Integer, default=1, comment="是否启用") remark = Column(String(500), nullable=True, comment="备注") created_at = Column(DateTime, server_default=func.now()) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) class VoucherDetail(Base): """凭证明细 — 新30号准则分类""" __tablename__ = "voucher_details" id = Column(Integer, primary_key=True, index=True) voucher_no = Column(String(50), nullable=False, comment="凭证编号") voucher_date = Column(DateTime, nullable=False, comment="凭证日期") subject_code = Column(String(20), nullable=False, comment="科目编码") subject_name = Column(String(200), nullable=True, comment="科目名称") debit_amount = Column(Float, default=0, comment="借方金额") credit_amount = Column(Float, default=0, comment="贷方金额") summary = Column(String(500), nullable=True, comment="摘要") new_standard_category = Column(String(20), nullable=True, comment="新30号准则分类") period = Column(String(20), nullable=True, comment="期间 YYYY-MM") created_at = Column(DateTime, server_default=func.now()) # ============================================================ # 费用审核智能体 (2026-08) # ============================================================ class ExpenseRule(Base): """费用规则 — 自动校验报销单的标准""" __tablename__ = "expense_rules" id = Column(Integer, primary_key=True, index=True) rule_name = Column(String(100), nullable=False, comment="规则名称") dimension = Column(String(20), nullable=False, comment="维度: department/person/expense_type") dimension_value = Column(String(100), nullable=True, comment="维度值: 部门名/人员名/费用类型(空=全局)") expense_type = Column(String(30), nullable=False, comment="费用类型: entertainment/travel/office/management") limit_type = Column(String(20), nullable=False, comment="限额类型: single/monthly/yearly 单笔/月度累计/年度累计") limit_amount = Column(Float, nullable=False, comment="限额金额") cycle = Column(String(20), default="monthly", comment="周期: single/monthly/yearly") status = Column(String(20), default="active", comment="active/inactive") remark = Column(String(500), nullable=True, comment="备注") created_at = Column(DateTime, server_default=func.now()) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) class ExpenseReimbursement(Base): """费用报销单 — 提交后自动校验规则,超限自动打回""" __tablename__ = "expense_reimbursements" id = Column(Integer, primary_key=True, index=True) reimb_no = Column(String(50), unique=True, nullable=False, comment="报销单号") applicant = Column(String(100), nullable=False, comment="申请人") department = Column(String(100), nullable=True, comment="部门") expense_type = Column(String(30), nullable=False, comment="费用类型: entertainment/travel/office/management") title = Column(String(200), nullable=False, comment="事由/摘要") amount = Column(Float, nullable=False, comment="报销金额") expense_date = Column(DateTime, nullable=True, comment="费用发生日期") attachment = Column(String(500), nullable=True, comment="附件文件名") status = Column(String(20), default="pending", comment="pending待审批/approved已通过/rejected已拒绝/returned已打回") check_result = Column(String(20), default="pass", comment="自动校验结果: pass/fail") check_reason = Column(String(1000), nullable=True, comment="超限原因") check_detail = Column(JSON, nullable=True, comment="校验明细: [{rule_name, limit, actual, passed}]") checked_at = Column(DateTime, nullable=True, comment="自动校验时间") approver = Column(String(100), nullable=True, comment="审批人") approve_comment = Column(String(500), nullable=True, comment="审批意见") approved_at = Column(DateTime, nullable=True, comment="审批时间") created_by = Column(String(100), nullable=True, comment="提交人") created_at = Column(DateTime, server_default=func.now()) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) # ============================================================ # 税务合规智能体 (2026-08) # ① 税负监控 ② 发票校验 ③ 社保比对 # ============================================================ class TaxRecord(Base): """税务记录 — 税负监控:应纳税额/实缴额/税负率 vs 行业基准""" __tablename__ = "tax_records" id = Column(Integer, primary_key=True, index=True) entity_id = Column(Integer, default=1, comment="企业ID") period = Column(String(20), nullable=False, comment="期间 YYYY-MM") tax_type = Column(String(30), nullable=False, comment="税种: vat增值税/income所得税/surtax附加税") tax_payable = Column(Float, default=0, comment="应纳税额") tax_paid = Column(Float, default=0, comment="实缴税额") tax_rate = Column(Float, nullable=True, comment="适用税率 %") income = Column(Float, default=0, comment="当期收入/计税收入(税负率分母)") tax_burden_rate = Column(Float, nullable=True, comment="税负率 % = 实缴税额/收入×100") burden_status = Column(String(20), default="normal", comment="normal正常/warning超基准±20%内/alert超基准±20%") warning_msg = Column(String(500), nullable=True, comment="预警信息") remark = Column(String(500), nullable=True, comment="备注") created_at = Column(DateTime, server_default=func.now()) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) class InvoiceCheck(Base): """发票校验 — 录入后按规则自动校验:号码格式/金额匹配报销单/供应商匹配合同""" __tablename__ = "invoice_check" id = Column(Integer, primary_key=True, index=True) entity_id = Column(Integer, default=1, comment="企业ID") invoice_no = Column(String(50), nullable=False, comment="发票号码") amount = Column(Float, nullable=False, comment="发票金额") invoice_type = Column(String(30), default="vat", comment="发票类型: vat专用/vat普通/electronic电子/other其他") invoice_date = Column(DateTime, nullable=True, comment="开票日期") supplier = Column(String(200), nullable=True, comment="供应商名称") reimb_no = Column(String(50), nullable=True, comment="关联报销单号") contract_no = Column(String(50), nullable=True, comment="关联合同编号") check_status = Column(String(20), default="pending", comment="校验状态: pending待校验/valid通过/invalid异常/warning提醒") check_result = Column(JSON, nullable=True, comment="校验明细: [{rule, passed, message}]") check_reason = Column(String(1000), nullable=True, comment="异常原因汇总") checked_at = Column(DateTime, nullable=True, comment="校验时间") created_at = Column(DateTime, server_default=func.now()) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) class SocialSecurity(Base): """社保缴费记录 — 社保比对:基数与工资匹配/单位缴纳比例/漏缴提醒""" __tablename__ = "social_security" id = Column(Integer, primary_key=True, index=True) entity_id = Column(Integer, default=1, comment="企业ID") employee = Column(String(100), nullable=False, comment="人员姓名") period = Column(String(20), nullable=False, comment="期间 YYYY-MM") base_amount = Column(Float, default=0, comment="缴费基数") salary = Column(Float, nullable=True, comment="申报工资") company_amount = Column(Float, default=0, comment="单位缴纳金额") personal_amount = Column(Float, default=0, comment="个人缴纳金额") company_rate = Column(Float, nullable=True, comment="单位缴纳比例 % (养老16%+医疗8%+失业0.5%≈24.5%)") check_status = Column(String(20), default="normal", comment="normal正常/warning基数或比例异常/alert漏缴") warning_msg = Column(String(500), nullable=True, comment="异常提醒: 漏缴/基数不符/比例异常") remark = Column(String(500), nullable=True, comment="备注") created_at = Column(DateTime, server_default=func.now()) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) # ============================================================ # 本体三支柱: 科目↔KPI↔OKR 三层互联 (2026-08-19) # 追溯链: 目标(O) → 指标(KPI) → 科目(数据) # ============================================================ class KPISubjectMap(Base): """科目↔KPI映射 — 指标计算依赖的底层会计科目""" __tablename__ = "kpi_subject_map" id = Column(Integer, primary_key=True, index=True) kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="KPI ID") subject_code = Column(String(20), nullable=False, comment="科目编码") calc_type = Column(String(20), default="sum", comment="sum/avg/ratio/other") weight = Column(Numeric(5, 2), default=1.00, comment="权重(负=扣减项)") remark = Column(String(200), nullable=True, comment="备注") __table_args__ = (UniqueConstraint("kpi_id", "subject_code", name="uk_kpi_subject"),) class ObjectiveKPI(Base): """KPI↔O支撑 — 目标由哪些KPI度量""" __tablename__ = "objective_kpi" id = Column(Integer, primary_key=True, index=True) entity_id = Column(Integer, default=1, comment="企业ID (P2多租户隔离 2026-08-23)") objective_id = Column(Integer, ForeignKey("objectives.id"), nullable=False, comment="OKR目标ID") kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=False, comment="KPI ID") weight = Column(Numeric(5, 2), default=1.00, comment="支撑权重") __table_args__ = (UniqueConstraint("objective_id", "kpi_id", name="uk_obj_kpi"),) class KR(Base): """关键结果KR — OKR完整化 (O→KR→KPI)""" __tablename__ = "krs" id = Column(Integer, primary_key=True, index=True) entity_id = Column(Integer, default=1, comment="企业ID (P2多租户隔离 2026-08-23)") objective_id = Column(Integer, ForeignKey("objectives.id"), nullable=False, comment="OKR目标ID") title = Column(String(200), nullable=False, comment="KR标题") metric_kpi_id = Column(Integer, ForeignKey("kpi_definitions.id"), nullable=True, comment="度量KPI ID") target_value = Column(Numeric(15, 2), nullable=True, comment="目标值") current_value = Column(Numeric(15, 2), nullable=True, comment="当前值") progress = Column(Integer, default=0, comment="完成进度 0-100") status = Column(String(20), default="pending", comment="pending/in_progress/completed/cancelled") due_date = Column(Date, nullable=True, comment="截止日期") created_at = Column(DateTime, server_default=func.now())