"""新30号准则适配 — 数据库迁移 创建 subjects 和 voucher_details 表,添加 new_standard_category 字段 """ import sys, os sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from app.database import get_engine, get_session_local from sqlalchemy import text import logging logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") logger = logging.getLogger("migrate_new30") def run(): engine = get_engine() with engine.connect() as conn: # 1. 创建 subjects 表(会计科目表) conn.execute(text(""" CREATE TABLE IF NOT EXISTS subjects ( id INT AUTO_INCREMENT PRIMARY KEY, subject_code VARCHAR(20) NOT NULL COMMENT '科目编码', subject_name VARCHAR(200) NOT NULL COMMENT '科目名称', parent_code VARCHAR(20) DEFAULT NULL COMMENT '上级科目编码', level INT DEFAULT 1 COMMENT '科目级别 1-4', category VARCHAR(50) DEFAULT NULL COMMENT '科目类别: asset/liability/equity/revenue/expense/profit_loss', new_standard_category VARCHAR(20) DEFAULT NULL COMMENT '新30号准则分类: operating/investing/financing/tax/discontinued', is_active TINYINT(1) DEFAULT 1 COMMENT '是否启用', remark VARCHAR(500) DEFAULT NULL COMMENT '备注', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_subject_code (subject_code) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会计科目表' """)) logger.info("✅ 创建 subjects 表") # 2. 创建 voucher_details 表(凭证明细表) conn.execute(text(""" CREATE TABLE IF NOT EXISTS voucher_details ( id INT AUTO_INCREMENT PRIMARY KEY, voucher_no VARCHAR(50) NOT NULL COMMENT '凭证编号', voucher_date DATE NOT NULL COMMENT '凭证日期', subject_code VARCHAR(20) NOT NULL COMMENT '科目编码', subject_name VARCHAR(200) DEFAULT NULL COMMENT '科目名称', debit_amount DECIMAL(18,2) DEFAULT 0 COMMENT '借方金额', credit_amount DECIMAL(18,2) DEFAULT 0 COMMENT '贷方金额', summary VARCHAR(500) DEFAULT NULL COMMENT '摘要', new_standard_category VARCHAR(20) DEFAULT NULL COMMENT '新30号准则分类: operating/investing/financing/tax/discontinued', period VARCHAR(20) DEFAULT NULL COMMENT '期间 YYYY-MM', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_voucher_no (voucher_no), INDEX idx_period (period), INDEX idx_subject_code (subject_code) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='凭证明细表' """)) logger.info("✅ 创建 voucher_details 表") # 3. 插入初始科目数据(新30号准则相关科目) seed_subjects = [ ('6001', '主营业务收入', None, 1, 'revenue', 'operating'), ('6051', '其他业务收入', None, 1, 'revenue', 'operating'), ('6401', '主营业务成本', None, 1, 'expense', 'operating'), ('6402', '其他业务成本', None, 1, 'expense', 'operating'), ('6601', '销售费用', None, 1, 'expense', 'operating'), ('6602', '管理费用', None, 1, 'expense', 'operating'), ('660204', '研发费用', '6602', 2, 'expense', 'operating_rd'), ('6603', '财务费用', None, 1, 'expense', 'operating_fx'), ('660301', '利息支出(借款)', '6603', 2, 'expense', 'financing'), ('660302', '筹资汇兑损益', '6603', 2, 'expense', 'financing_fx'), ('6011', '利息收入(银行存款)', None, 1, 'revenue', 'investing'), ('6111', '投资收益', None, 1, 'revenue', 'investing'), ('611101', '股权投资', '6111', 2, 'revenue', 'investing'), ('6701', '资产减值损失', None, 1, 'expense', 'operating'), ('670101', '投资类资产减值', '6701', 2, 'expense', 'investing'), ('6801', '所得税费用', None, 1, 'expense', 'tax'), ('6901', '终止经营损益', None, 1, 'profit_loss', 'discontinued'), ] for row in seed_subjects: conn.execute(text(""" INSERT IGNORE INTO subjects (subject_code, subject_name, parent_code, level, category, new_standard_category, is_active) VALUES (:code, :name, :parent, :level, :cat, :ns_cat, 1) """), {"code": row[0], "name": row[1], "parent": row[2], "level": row[3], "cat": row[4], "ns_cat": row[5]}) logger.info(f"✅ 插入 {len(seed_subjects)} 条初始科目数据") conn.commit() logger.info("🎉 新30号准则数据库迁移完成") if __name__ == "__main__": run()