1032 行
29 KiB
JavaScript
1032 行
29 KiB
JavaScript
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_TRAVEL_WORKFLOW_DEFINITION = {
|
|
steps: [
|
|
{
|
|
id: "travel_approval_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: "TR_STANDARD",
|
|
name: "标准差旅申请",
|
|
entity_type: "travel_request",
|
|
sequence_prefix: "CL",
|
|
number_length: 4,
|
|
reset_period: "yearly",
|
|
default_workflow_key: null,
|
|
enabled: true,
|
|
},
|
|
{
|
|
code: "TR_RECON",
|
|
name: "差旅对账批次",
|
|
entity_type: "travel_reconciliation_batch",
|
|
sequence_prefix: "TRB",
|
|
number_length: 4,
|
|
reset_period: "yearly",
|
|
default_workflow_key: null,
|
|
enabled: true,
|
|
},
|
|
{
|
|
code: "ASSET_CARD_DEFAULT",
|
|
name: "固定资产卡片",
|
|
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: "ai_assist",
|
|
override_mode: "force_off",
|
|
rollout_type: "global",
|
|
department_ids: [],
|
|
note: "bootstrap default: force_off",
|
|
},
|
|
{
|
|
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_on",
|
|
rollout_type: "global",
|
|
department_ids: [],
|
|
note: "bootstrap default: force_on",
|
|
},
|
|
{
|
|
flag_key: "budget_precheck_v1",
|
|
override_mode: "force_off",
|
|
rollout_type: "global",
|
|
department_ids: [],
|
|
note: "bootstrap default: force_off",
|
|
},
|
|
{
|
|
flag_key: "payment_batch_v1",
|
|
override_mode: "force_off",
|
|
rollout_type: "global",
|
|
department_ids: [],
|
|
note: "bootstrap default: force_off",
|
|
},
|
|
{
|
|
flag_key: "portal_home_v1",
|
|
override_mode: "force_off",
|
|
rollout_type: "global",
|
|
department_ids: [],
|
|
note: "bootstrap default: force_off",
|
|
},
|
|
{
|
|
flag_key: "global_search_v1",
|
|
override_mode: "force_off",
|
|
rollout_type: "global",
|
|
department_ids: [],
|
|
note: "bootstrap default: force_off",
|
|
},
|
|
{
|
|
flag_key: "announcements_v1",
|
|
override_mode: "force_off",
|
|
rollout_type: "global",
|
|
department_ids: [],
|
|
note: "bootstrap default: force_off",
|
|
},
|
|
{
|
|
flag_key: "workflow_definition_admin_v1",
|
|
override_mode: "force_off",
|
|
rollout_type: "global",
|
|
department_ids: [],
|
|
note: "bootstrap default: force_off",
|
|
},
|
|
{
|
|
flag_key: "cutover_console_v1",
|
|
override_mode: "force_off",
|
|
rollout_type: "global",
|
|
department_ids: [],
|
|
note: "bootstrap default: force_off",
|
|
},
|
|
{
|
|
flag_key: "presence_board_v1",
|
|
override_mode: "force_off",
|
|
rollout_type: "global",
|
|
department_ids: [],
|
|
note: "bootstrap default: force_off",
|
|
},
|
|
{
|
|
flag_key: "attendance_v1",
|
|
override_mode: "force_off",
|
|
rollout_type: "global",
|
|
department_ids: [],
|
|
note: "bootstrap default: force_off",
|
|
},
|
|
{
|
|
flag_key: "attendance_unified_v1",
|
|
override_mode: "force_off",
|
|
rollout_type: "global",
|
|
department_ids: [],
|
|
note: "bootstrap default: force_off",
|
|
},
|
|
{
|
|
flag_key: "weekly_report_v1",
|
|
override_mode: "force_off",
|
|
rollout_type: "global",
|
|
department_ids: [],
|
|
note: "bootstrap default: force_off",
|
|
},
|
|
{
|
|
flag_key: "employee_lifecycle_v1",
|
|
override_mode: "force_off",
|
|
rollout_type: "global",
|
|
department_ids: [],
|
|
note: "bootstrap default: force_off",
|
|
},
|
|
{
|
|
flag_key: "todo_board_v1",
|
|
override_mode: "force_off",
|
|
rollout_type: "global",
|
|
department_ids: [],
|
|
note: "bootstrap default: force_off",
|
|
},
|
|
{
|
|
flag_key: "wecom_entry_v1",
|
|
override_mode: "force_off",
|
|
rollout_type: "global",
|
|
department_ids: [],
|
|
note: "bootstrap default: force_off",
|
|
},
|
|
{
|
|
flag_key: "project_domain_v1",
|
|
override_mode: "force_off",
|
|
rollout_type: "global",
|
|
department_ids: [],
|
|
note: "bootstrap default: force_off",
|
|
},
|
|
{
|
|
flag_key: "project_linking_v1",
|
|
override_mode: "force_off",
|
|
rollout_type: "global",
|
|
department_ids: [],
|
|
note: "bootstrap default: force_off",
|
|
},
|
|
{
|
|
flag_key: "project_communication_log_v1",
|
|
override_mode: "force_off",
|
|
rollout_type: "global",
|
|
department_ids: [],
|
|
note: "bootstrap default: force_off",
|
|
},
|
|
{
|
|
flag_key: "project_closure_checklist_v1",
|
|
override_mode: "force_off",
|
|
rollout_type: "global",
|
|
department_ids: [],
|
|
note: "bootstrap default: force_off",
|
|
},
|
|
{
|
|
flag_key: "confidential_doc_refs_v1",
|
|
override_mode: "force_off",
|
|
rollout_type: "global",
|
|
department_ids: [],
|
|
note: "bootstrap default: force_off",
|
|
},
|
|
{
|
|
flag_key: "public_security_hardening_v1",
|
|
override_mode: "force_off",
|
|
rollout_type: "global",
|
|
department_ids: [],
|
|
note: "bootstrap default: force_off",
|
|
},
|
|
];
|
|
|
|
const DEFAULT_BUDGET_CONTROL_RULES = [
|
|
{
|
|
name: "默认全局预算预警",
|
|
entity_type: "all",
|
|
department_id: null,
|
|
legal_entity_id: null,
|
|
cost_center_id: null,
|
|
expense_type_id: null,
|
|
control_mode: "warn",
|
|
priority: 100,
|
|
enabled: true,
|
|
notes: "bootstrap default global warn rule",
|
|
},
|
|
];
|
|
|
|
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 天。",
|
|
},
|
|
];
|
|
|
|
const OFFICIAL_2026_HOLIDAY_CALENDAR = {
|
|
code: "CN_2026_STD",
|
|
name: "2026年中国法定节假日",
|
|
timezone: "Asia/Shanghai",
|
|
days: [
|
|
{ date: "2026-01-01", dayType: "holiday", name: "元旦" },
|
|
{ date: "2026-01-02", dayType: "holiday", name: "元旦假期" },
|
|
{ date: "2026-01-03", dayType: "holiday", name: "元旦假期" },
|
|
{ date: "2026-01-04", dayType: "adjusted_workday", name: "元旦调休补班" },
|
|
{ date: "2026-02-14", dayType: "adjusted_workday", name: "春节调休补班" },
|
|
{ date: "2026-02-15", dayType: "holiday", name: "春节" },
|
|
{ date: "2026-02-16", dayType: "holiday", name: "春节假期" },
|
|
{ date: "2026-02-17", dayType: "holiday", name: "春节假期" },
|
|
{ date: "2026-02-18", dayType: "holiday", name: "春节假期" },
|
|
{ date: "2026-02-19", dayType: "holiday", name: "春节假期" },
|
|
{ date: "2026-02-20", dayType: "holiday", name: "春节假期" },
|
|
{ date: "2026-02-21", dayType: "holiday", name: "春节假期" },
|
|
{ date: "2026-02-22", dayType: "holiday", name: "春节假期" },
|
|
{ date: "2026-02-23", dayType: "holiday", name: "春节假期" },
|
|
{ date: "2026-02-28", dayType: "adjusted_workday", name: "春节调休补班" },
|
|
{ date: "2026-04-04", dayType: "holiday", name: "清明节" },
|
|
{ date: "2026-04-05", dayType: "holiday", name: "清明节假期" },
|
|
{ date: "2026-04-06", dayType: "holiday", name: "清明节假期" },
|
|
{ date: "2026-05-01", dayType: "holiday", name: "劳动节" },
|
|
{ date: "2026-05-02", dayType: "holiday", name: "劳动节假期" },
|
|
{ date: "2026-05-03", dayType: "holiday", name: "劳动节假期" },
|
|
{ date: "2026-05-04", dayType: "holiday", name: "劳动节假期" },
|
|
{ date: "2026-05-05", dayType: "holiday", name: "劳动节假期" },
|
|
{ date: "2026-05-09", dayType: "adjusted_workday", name: "劳动节调休补班" },
|
|
{ date: "2026-06-19", dayType: "holiday", name: "端午节" },
|
|
{ date: "2026-06-20", dayType: "holiday", name: "端午节假期" },
|
|
{ date: "2026-06-21", dayType: "holiday", name: "端午节假期" },
|
|
{ date: "2026-09-20", dayType: "adjusted_workday", name: "国庆节调休补班" },
|
|
{ date: "2026-09-25", dayType: "holiday", name: "中秋节" },
|
|
{ date: "2026-09-26", dayType: "holiday", name: "中秋节假期" },
|
|
{ date: "2026-09-27", dayType: "holiday", name: "中秋节假期" },
|
|
{ date: "2026-10-01", dayType: "holiday", name: "国庆节" },
|
|
{ date: "2026-10-02", dayType: "holiday", name: "国庆节假期" },
|
|
{ date: "2026-10-03", dayType: "holiday", name: "国庆节假期" },
|
|
{ date: "2026-10-04", dayType: "holiday", name: "国庆节假期" },
|
|
{ date: "2026-10-05", dayType: "holiday", name: "国庆节假期" },
|
|
{ date: "2026-10-06", dayType: "holiday", name: "国庆节假期" },
|
|
{ date: "2026-10-07", dayType: "holiday", name: "国庆节假期" },
|
|
{ date: "2026-10-10", dayType: "adjusted_workday", name: "国庆节调休补班" },
|
|
],
|
|
};
|
|
|
|
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 seedOfficialHolidayCalendar(prisma) {
|
|
const now = new Date();
|
|
const existing = await prisma.holidayCalendar.findUnique({
|
|
where: { code: OFFICIAL_2026_HOLIDAY_CALENDAR.code },
|
|
select: { id: true },
|
|
});
|
|
|
|
const calendar = existing
|
|
? await prisma.holidayCalendar.update({
|
|
where: { id: existing.id },
|
|
data: {
|
|
name: OFFICIAL_2026_HOLIDAY_CALENDAR.name,
|
|
timezone: OFFICIAL_2026_HOLIDAY_CALENDAR.timezone,
|
|
legal_entity_id: null,
|
|
enabled: true,
|
|
archived_at: null,
|
|
updated_at: now,
|
|
updated_by: null,
|
|
},
|
|
})
|
|
: await prisma.holidayCalendar.create({
|
|
data: {
|
|
code: OFFICIAL_2026_HOLIDAY_CALENDAR.code,
|
|
name: OFFICIAL_2026_HOLIDAY_CALENDAR.name,
|
|
timezone: OFFICIAL_2026_HOLIDAY_CALENDAR.timezone,
|
|
legal_entity_id: null,
|
|
enabled: true,
|
|
archived_at: null,
|
|
created_at: now,
|
|
created_by: null,
|
|
updated_at: now,
|
|
updated_by: null,
|
|
},
|
|
});
|
|
|
|
await prisma.holidayCalendarDay.deleteMany({
|
|
where: { calendar_id: calendar.id },
|
|
});
|
|
|
|
await prisma.holidayCalendarDay.createMany({
|
|
data: OFFICIAL_2026_HOLIDAY_CALENDAR.days.map((item) => ({
|
|
calendar_id: calendar.id,
|
|
date: new Date(`${item.date}T00:00:00.000Z`),
|
|
day_type: item.dayType,
|
|
name: item.name,
|
|
note: null,
|
|
created_at: now,
|
|
created_by: null,
|
|
updated_at: now,
|
|
updated_by: null,
|
|
})),
|
|
});
|
|
|
|
const defaultLegalEntity = await prisma.legalEntity.findFirst({
|
|
where: {
|
|
enabled: true,
|
|
archived_at: null,
|
|
},
|
|
orderBy: {
|
|
created_at: "asc",
|
|
},
|
|
select: {
|
|
id: true,
|
|
default_holiday_calendar_id: true,
|
|
},
|
|
});
|
|
|
|
if (defaultLegalEntity && !defaultLegalEntity.default_holiday_calendar_id) {
|
|
await prisma.legalEntity.update({
|
|
where: { id: defaultLegalEntity.id },
|
|
data: {
|
|
default_holiday_calendar_id: calendar.id,
|
|
updated_at: now,
|
|
updated_by: null,
|
|
},
|
|
});
|
|
}
|
|
|
|
return {
|
|
calendar,
|
|
dayCount: OFFICIAL_2026_HOLIDAY_CALENDAR.days.length,
|
|
boundLegalEntityId:
|
|
defaultLegalEntity && !defaultLegalEntity.default_holiday_calendar_id
|
|
? defaultLegalEntity.id
|
|
: null,
|
|
};
|
|
}
|
|
|
|
async function seedAttendancePolicy(prisma) {
|
|
const now = new Date();
|
|
const current = await prisma.attendancePolicy.findFirst({
|
|
orderBy: { created_at: "asc" },
|
|
select: { id: true },
|
|
});
|
|
|
|
if (current) {
|
|
await prisma.attendancePolicy.update({
|
|
where: { id: current.id },
|
|
data: {
|
|
work_start_time: "09:00",
|
|
work_end_time: "18:00",
|
|
lunch_start_time: "12:00",
|
|
lunch_end_time: "13:00",
|
|
late_tolerance_minutes: 0,
|
|
enabled: true,
|
|
updated_at: now,
|
|
updated_by: null,
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
await prisma.attendancePolicy.create({
|
|
data: {
|
|
work_start_time: "09:00",
|
|
work_end_time: "18:00",
|
|
lunch_start_time: "12:00",
|
|
lunch_end_time: "13:00",
|
|
late_tolerance_minutes: 0,
|
|
enabled: true,
|
|
created_at: now,
|
|
updated_at: now,
|
|
updated_by: null,
|
|
},
|
|
});
|
|
}
|
|
|
|
async function seedBudgetControlRules(prisma) {
|
|
const now = new Date();
|
|
|
|
for (const item of DEFAULT_BUDGET_CONTROL_RULES) {
|
|
const existing = await prisma.budgetControlRule.findFirst({
|
|
where: {
|
|
name: item.name,
|
|
entity_type: item.entity_type,
|
|
archived_at: null,
|
|
},
|
|
select: { id: true },
|
|
});
|
|
|
|
if (existing) {
|
|
await prisma.budgetControlRule.update({
|
|
where: { id: existing.id },
|
|
data: {
|
|
department_id: item.department_id,
|
|
legal_entity_id: item.legal_entity_id,
|
|
cost_center_id: item.cost_center_id,
|
|
expense_type_id: item.expense_type_id,
|
|
control_mode: item.control_mode,
|
|
priority: item.priority,
|
|
enabled: item.enabled,
|
|
notes: item.notes,
|
|
archived_at: null,
|
|
updated_at: now,
|
|
updated_by: null,
|
|
},
|
|
});
|
|
continue;
|
|
}
|
|
|
|
await prisma.budgetControlRule.create({
|
|
data: {
|
|
...item,
|
|
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,
|
|
},
|
|
});
|
|
|
|
await prisma.workflowDefinition.updateMany({
|
|
where: {
|
|
entity_type: "travel_request",
|
|
},
|
|
data: {
|
|
is_active: false,
|
|
},
|
|
});
|
|
|
|
await prisma.workflowDefinition.upsert({
|
|
where: {
|
|
entity_type_version: {
|
|
entity_type: "travel_request",
|
|
version: 1,
|
|
},
|
|
},
|
|
update: {
|
|
name: "差旅申请审批流程(V1)",
|
|
definition_json: DEFAULT_TRAVEL_WORKFLOW_DEFINITION,
|
|
is_active: true,
|
|
created_by: null,
|
|
},
|
|
create: {
|
|
name: "差旅申请审批流程(V1)",
|
|
entity_type: "travel_request",
|
|
definition_json: DEFAULT_TRAVEL_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);
|
|
const holidayCalendarSeed = await seedOfficialHolidayCalendar(prisma);
|
|
await seedAttendancePolicy(prisma);
|
|
await seedBudgetControlRules(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("- workflow definition: travel_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`);
|
|
// eslint-disable-next-line no-console
|
|
console.log(
|
|
`- holiday calendar: ${holidayCalendarSeed.calendar.code} / ${holidayCalendarSeed.dayCount} days seeded${
|
|
holidayCalendarSeed.boundLegalEntityId
|
|
? ` / bound legal entity ${holidayCalendarSeed.boundLegalEntityId}`
|
|
: ""
|
|
}`,
|
|
);
|
|
// eslint-disable-next-line no-console
|
|
console.log("- attendance policy: default 09:00-18:00 seeded");
|
|
} finally {
|
|
await prisma.$disconnect();
|
|
await pool.end();
|
|
}
|
|
}
|
|
|
|
main().catch((error) => {
|
|
// eslint-disable-next-line no-console
|
|
console.error(error);
|
|
process.exit(1);
|
|
});
|