Files
cma-management/backend/app/models/__init__.py
T
Hermes CI Fix abedf8cb8d feat: Bot KPI管理体系 — bot_source字段 + 11个财务Bot KPI + Bot KPI看板
- 新增 bot_source 字段到 kpi_definitions 表(DB迁移 + 模型字段)
- 创建 bot_kpis.py API(GET /api/cma/bot-kpis + POST .../value)
- 种子脚本 seed_finance_bot_kpis.py 插入11个财务Bot KPI
- BotKpiDashboard.vue 看板组件(三区:核心产出5/质量3/用户反馈3)
- 路由 /bot-kpis + 侧边栏菜单入口
- 复用五档评分引擎
2026-07-25 07:44:41 +08:00

497 lines
27 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""管理会计OS 数据模型"""
from sqlalchemy import Column, Integer, String, Text, Float, DateTime, ForeignKey, Boolean, JSON, func
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 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="目标值")
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="负责人")
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)
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)
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="改善结果")
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="验证历史日志")
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")
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 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 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())