R62 A1-A6:2026年中国法定假期日历

这个提交包含在:
2026-04-12 21:31:24 +08:00
父节点 97b679033e
当前提交 f8d941ec64
共修改 10 个文件,包含 638 行新增和 31 行删除
+146 -2
查看文件
@@ -228,10 +228,10 @@ const DEFAULT_FEATURE_FLAGS = [
},
{
flag_key: "leave_holiday_calendar",
override_mode: "force_off",
override_mode: "force_on",
rollout_type: "global",
department_ids: [],
note: "bootstrap default: force_off",
note: "bootstrap default: force_on",
},
{
flag_key: "budget_precheck_v1",
@@ -419,6 +419,53 @@ const DEFAULT_LEAVE_TYPES = [
},
];
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 = [
@@ -559,6 +606,94 @@ async function seedFeatureFlags(prisma) {
}
}
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({
@@ -840,6 +975,7 @@ async function main() {
await seedWorkflowDefinition(prisma);
await seedLeaveTypes(prisma);
await seedFeatureFlags(prisma);
const holidayCalendarSeed = await seedOfficialHolidayCalendar(prisma);
await seedAttendancePolicy(prisma);
await seedBudgetControlRules(prisma);
@@ -866,6 +1002,14 @@ async function main() {
// 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();