require("dotenv/config"); const { createPrismaClient } = require("../prismaClient"); const ENABLED_EXPENSE_TYPES = [ { code: "travel_airfare", name: "机票", category: "travel" }, { code: "travel_train", name: "火车票", category: "travel" }, { code: "travel_taxi", name: "打车费", category: "travel" }, { code: "travel_hotel", name: "住宿费", category: "travel" }, { code: "travel_bus", name: "市内交通", category: "travel" }, { code: "travel_meal", name: "差旅餐补", category: "travel" }, { code: "travel_fuel", name: "油费", category: "travel" }, { code: "travel_toll", name: "过路费", category: "travel" }, { code: "travel_parking", name: "停车费", category: "travel" }, { code: "travel_visa", name: "签证费", category: "travel" }, { code: "rd_materials", name: "研发材料", category: "rd" }, { code: "rd_testing", name: "测试费用", category: "rd" }, { code: "rd_prototype", name: "样机制作", category: "rd" }, { code: "rd_software", name: "软件订阅", category: "rd" }, { code: "rd_external_service", name: "外部技术服务", category: "rd" }, { code: "production_raw_material", name: "生产原材料", category: "production" }, { code: "production_processing", name: "加工费", category: "production" }, { code: "production_quality", name: "质量检测", category: "production" }, { code: "production_maintenance", name: "设备维护", category: "production" }, { code: "production_energy", name: "生产能耗", category: "production" }, { code: "office_stationery", name: "文具耗材", category: "office" }, { code: "office_supplies", name: "办公用品", category: "office" }, { code: "office_property_fee", name: "物业费用", category: "office" }, { code: "office_communication", name: "通讯费用", category: "office" }, { code: "office_postal", name: "邮寄快递", category: "office" }, { code: "office_cleaning", name: "保洁服务", category: "office" }, { code: "business_client_meal", name: "客户招待", category: "business" }, { code: "business_gift", name: "商务礼品", category: "business" }, { code: "business_event", name: "活动执行", category: "business" }, { code: "business_marketing", name: "营销投放", category: "business" }, { code: "business_training", name: "培训会议", category: "business" }, { code: "assets_equipment", name: "设备采购", category: "assets" }, { code: "assets_furniture", name: "家具采购", category: "assets" }, { code: "assets_it_hardware", name: "IT 硬件", category: "assets" }, { code: "utilities", name: "水电费", category: "other" }, { code: "rent", name: "房租租赁费", category: "other" }, { code: "other_misc", name: "其他杂项", category: "other" }, ]; const DISABLED_LEGACY_EXPENSE_TYPES = [ { code: "transport", name: "legacy-交通费", category: "travel" }, { code: "meal", name: "legacy-餐费", category: "business" }, { code: "rd", name: "legacy-研发费", category: "rd" }, ]; const DEFAULT_APPROVAL_RULES = [ { name: "小额", min_amount: "0", max_amount: "500", level1_role: "manager", level2_role: "finance", level3_role: null, priority: 10, }, { name: "中额", min_amount: "500", max_amount: "5000", level1_role: "manager", level2_role: "finance_mgr", level3_role: null, priority: 20, }, { name: "大额", min_amount: "5000", max_amount: null, level1_role: "manager", level2_role: "ceo", level3_role: "finance_mgr", priority: 30, }, ]; const DEFAULT_WORKFLOW_DEFINITION = { steps: [ { id: "ceo_review", name: "总经理审批", type: "approval", assignee_rule: { type: "role", value: "ceo", }, }, ], }; const DEFAULT_LEAVE_WORKFLOW_DEFINITION = { steps: [ { id: "leave_approval_manager", name: "部门审批", type: "approval", assignee_rule: { type: "department_manager", }, }, ], }; const DEFAULT_PURCHASE_WORKFLOW_DEFINITION = { steps: [ { id: "approval_1_manager", name: "直属经理审批", type: "approval", assignee_rule: { type: "department_manager", }, }, ], }; const DEFAULT_DOCUMENT_CATEGORIES = [ { code: "reimbursement_general", name: "通用报销单", entity_type: "reimbursement", sequence_prefix: "BX", number_length: 4, reset_period: "yearly", default_workflow_key: null, enabled: true, }, { code: "leave_request", name: "请假申请单", entity_type: "leave", sequence_prefix: "JQ", number_length: 4, reset_period: "yearly", default_workflow_key: null, enabled: true, }, { code: "PR_STANDARD", name: "标准采购申请", entity_type: "purchase_request", sequence_prefix: "CG", number_length: 4, reset_period: "yearly", default_workflow_key: null, enabled: true, }, { code: "asset_it", name: "IT 固资台账", entity_type: "asset_card", sequence_prefix: "ZC", number_length: 4, reset_period: "yearly", default_workflow_key: null, enabled: true, }, { code: "general_record", name: "通用业务单据", entity_type: "general", sequence_prefix: "GEN", number_length: 4, reset_period: "yearly", default_workflow_key: null, enabled: true, }, ]; const DEFAULT_FEATURE_FLAGS = [ { flag_key: "erp_sync", override_mode: "force_off", rollout_type: "global", department_ids: [], note: "bootstrap default: force_off", }, { flag_key: "invoice_verification", override_mode: "force_off", rollout_type: "global", department_ids: [], note: "bootstrap default: force_off", }, { flag_key: "leave_holiday_calendar", override_mode: "force_off", rollout_type: "global", department_ids: [], note: "bootstrap default: force_off", }, ]; const DEFAULT_LEAVE_TYPES = [ { code: "annual_leave", name: "年假", description: "按自然日+半天粒度扣减。", enabled: true, requires_attachment: false, attachment_mime_types: null, sort_order: 10, annual_quota_days: "15.0", policy_description: "默认年度授予 15 天。", }, { code: "sick_leave", name: "病假", description: "病假需上传诊断或病历附件。", enabled: true, requires_attachment: true, attachment_mime_types: ["application/pdf", "image/jpeg", "image/png"], sort_order: 20, annual_quota_days: "10.0", policy_description: "默认年度授予 10 天。", }, { code: "personal_leave", name: "事假", description: "事假默认走经理审批。", enabled: true, requires_attachment: false, attachment_mime_types: null, sort_order: 30, annual_quota_days: "5.0", policy_description: "默认年度授予 5 天。", }, ]; async function seedExpenseTypes(prisma) { const now = new Date(); const allTypes = [ ...ENABLED_EXPENSE_TYPES.map((item) => ({ ...item, enabled: true })), ...DISABLED_LEGACY_EXPENSE_TYPES.map((item) => ({ ...item, enabled: false })), ]; for (const [index, item] of allTypes.entries()) { await prisma.expenseType.upsert({ where: { code: item.code }, update: { name: item.name, description: null, category: item.category, sort_order: index + 1, account_code_default: null, is_system_default: true, enabled: item.enabled, updated_at: now, updated_by: null, }, create: { code: item.code, name: item.name, description: null, category: item.category, sort_order: index + 1, account_code_default: null, is_system_default: true, enabled: item.enabled, created_at: now, created_by: null, updated_at: now, updated_by: null, }, }); } } async function seedApprovalRules(prisma) { await prisma.approvalRule.deleteMany({}); await prisma.approvalRule.createMany({ data: DEFAULT_APPROVAL_RULES.map((item) => ({ ...item, enabled: true, created_by: null, updated_by: null, })), }); } async function seedDocumentSequence(prisma) { await prisma.documentSequence.upsert({ where: { document_type: "reimbursement" }, update: { prefix: "BX", date_format: "YYYYMMDD", separator: "-", number_length: 4, reset_period: "none", }, create: { document_type: "reimbursement", prefix: "BX", date_format: "YYYYMMDD", separator: "-", number_length: 4, reset_period: "none", current_number: 0, updated_by: null, }, }); } async function seedDocumentCategories(prisma) { const now = new Date(); for (const item of DEFAULT_DOCUMENT_CATEGORIES) { await prisma.documentCategory.upsert({ where: { code: item.code }, update: { name: item.name, entity_type: item.entity_type, sequence_prefix: item.sequence_prefix, number_length: item.number_length, reset_period: item.reset_period, default_workflow_key: item.default_workflow_key, enabled: item.enabled, archived_at: null, updated_at: now, updated_by: null, }, create: { code: item.code, name: item.name, entity_type: item.entity_type, sequence_prefix: item.sequence_prefix, number_length: item.number_length, reset_period: item.reset_period, default_workflow_key: item.default_workflow_key, enabled: item.enabled, archived_at: null, created_at: now, created_by: null, updated_at: now, updated_by: null, }, }); } } async function seedFeatureFlags(prisma) { const now = new Date(); for (const item of DEFAULT_FEATURE_FLAGS) { await prisma.featureFlag.upsert({ where: { flag_key: item.flag_key }, update: { override_mode: item.override_mode, rollout_type: item.rollout_type, department_ids: item.department_ids, note: item.note, updated_at: now, updated_by: null, }, create: { flag_key: item.flag_key, override_mode: item.override_mode, rollout_type: item.rollout_type, department_ids: item.department_ids, note: item.note, created_at: now, created_by: null, updated_at: now, updated_by: null, }, }); } } async function seedLeaveTypes(prisma) { const now = new Date(); for (const item of DEFAULT_LEAVE_TYPES) { const leaveType = await prisma.leaveType.upsert({ where: { code: item.code }, update: { name: item.name, description: item.description, enabled: item.enabled, requires_attachment: item.requires_attachment, attachment_mime_types: item.attachment_mime_types, sort_order: item.sort_order, updated_at: now, updated_by: null, }, create: { code: item.code, name: item.name, description: item.description, enabled: item.enabled, requires_attachment: item.requires_attachment, attachment_mime_types: item.attachment_mime_types, sort_order: item.sort_order, created_at: now, created_by: null, updated_at: now, updated_by: null, }, select: { id: true }, }); await prisma.leavePolicy.upsert({ where: { leave_type_id: leaveType.id }, update: { annual_quota_days: item.annual_quota_days, enabled: item.enabled, description: item.policy_description, updated_at: now, updated_by: null, }, create: { leave_type_id: leaveType.id, annual_quota_days: item.annual_quota_days, enabled: item.enabled, description: item.policy_description, created_at: now, created_by: null, updated_at: now, updated_by: null, }, }); } } async function seedWorkflowDefinition(prisma) { await prisma.workflowDefinition.updateMany({ where: { entity_type: "reimbursement", }, data: { is_active: false, }, }); await prisma.workflowDefinition.upsert({ where: { entity_type_version: { entity_type: "reimbursement", version: 1, }, }, update: { name: "报销审批流程(MVP)", definition_json: DEFAULT_WORKFLOW_DEFINITION, is_active: true, created_by: null, }, create: { name: "报销审批流程(MVP)", entity_type: "reimbursement", definition_json: DEFAULT_WORKFLOW_DEFINITION, version: 1, is_active: true, created_by: null, }, }); await prisma.workflowDefinition.updateMany({ where: { entity_type: "leave", }, data: { is_active: false, }, }); await prisma.workflowDefinition.upsert({ where: { entity_type_version: { entity_type: "leave", version: 1, }, }, update: { name: "请假审批流程(MVP)", definition_json: DEFAULT_LEAVE_WORKFLOW_DEFINITION, is_active: true, created_by: null, }, create: { name: "请假审批流程(MVP)", entity_type: "leave", definition_json: DEFAULT_LEAVE_WORKFLOW_DEFINITION, version: 1, is_active: true, created_by: null, }, }); await prisma.workflowDefinition.updateMany({ where: { entity_type: "purchase_request", }, data: { is_active: false, }, }); await prisma.workflowDefinition.upsert({ where: { entity_type_version: { entity_type: "purchase_request", version: 1, }, }, update: { name: "采购申请审批流程(V1)", definition_json: DEFAULT_PURCHASE_WORKFLOW_DEFINITION, is_active: true, created_by: null, }, create: { name: "采购申请审批流程(V1)", entity_type: "purchase_request", definition_json: DEFAULT_PURCHASE_WORKFLOW_DEFINITION, version: 1, is_active: true, created_by: null, }, }); } async function main() { const { prisma, pool } = createPrismaClient(); await prisma.$connect(); try { await seedExpenseTypes(prisma); await seedApprovalRules(prisma); await seedDocumentSequence(prisma); await seedDocumentCategories(prisma); await seedWorkflowDefinition(prisma); await seedLeaveTypes(prisma); await seedFeatureFlags(prisma); // eslint-disable-next-line no-console console.log("Seeded config data:"); // eslint-disable-next-line no-console console.log(`- expense types: ${ENABLED_EXPENSE_TYPES.length} enabled + ${DISABLED_LEGACY_EXPENSE_TYPES.length} disabled`); // eslint-disable-next-line no-console console.log("- approval rules: 3"); // eslint-disable-next-line no-console console.log("- document sequence: reimbursement(reset_period=none)"); // eslint-disable-next-line no-console console.log(`- document categories: ${DEFAULT_DOCUMENT_CATEGORIES.length} seeded`); // eslint-disable-next-line no-console console.log("- workflow definition: reimbursement v1 (1-step role=ceo)"); // eslint-disable-next-line no-console console.log("- workflow definition: leave v1 (1-step department_manager)"); // eslint-disable-next-line no-console console.log("- workflow definition: purchase_request v1 (1-step department_manager)"); // eslint-disable-next-line no-console console.log(`- leave types: ${DEFAULT_LEAVE_TYPES.length} seeded with default policies`); // eslint-disable-next-line no-console console.log(`- feature flags: ${DEFAULT_FEATURE_FLAGS.length} seeded`); } finally { await prisma.$disconnect(); await pool.end(); } } main().catch((error) => { // eslint-disable-next-line no-console console.error(error); process.exit(1); });