feat(G1,P16A,P16B): 收口主数据核心与节假日日历增强
这个提交包含在:
@@ -33,6 +33,147 @@ async function ensureDepartment(prisma, payload) {
|
||||
});
|
||||
}
|
||||
|
||||
async function ensureLegalEntity(prisma, payload) {
|
||||
const now = new Date();
|
||||
const existing = await prisma.legalEntity.findUnique({
|
||||
where: { code: payload.code },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
return prisma.legalEntity.update({
|
||||
where: { id: existing.id },
|
||||
data: {
|
||||
name: payload.name,
|
||||
short_name: payload.shortName ?? null,
|
||||
currency_code: payload.currencyCode,
|
||||
tax_no: payload.taxNo ?? null,
|
||||
enabled: true,
|
||||
archived_at: null,
|
||||
updated_at: now,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return prisma.legalEntity.create({
|
||||
data: {
|
||||
code: payload.code,
|
||||
name: payload.name,
|
||||
short_name: payload.shortName ?? null,
|
||||
currency_code: payload.currencyCode,
|
||||
tax_no: payload.taxNo ?? null,
|
||||
enabled: true,
|
||||
archived_at: null,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function ensureCostCenter(prisma, payload) {
|
||||
const now = new Date();
|
||||
const existing = await prisma.costCenter.findFirst({
|
||||
where: {
|
||||
legal_entity_id: payload.legalEntityId,
|
||||
code: payload.code,
|
||||
},
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
return prisma.costCenter.update({
|
||||
where: { id: existing.id },
|
||||
data: {
|
||||
name: payload.name,
|
||||
parent_id: payload.parentId ?? null,
|
||||
owner_employee_id: payload.ownerEmployeeId ?? null,
|
||||
enabled: true,
|
||||
archived_at: null,
|
||||
updated_at: now,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return prisma.costCenter.create({
|
||||
data: {
|
||||
legal_entity_id: payload.legalEntityId,
|
||||
code: payload.code,
|
||||
name: payload.name,
|
||||
parent_id: payload.parentId ?? null,
|
||||
owner_employee_id: payload.ownerEmployeeId ?? null,
|
||||
enabled: true,
|
||||
archived_at: null,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function ensureHolidayCalendar(prisma, payload) {
|
||||
const now = new Date();
|
||||
const existing = await prisma.holidayCalendar.findUnique({
|
||||
where: { code: payload.code },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
const calendar = existing
|
||||
? await prisma.holidayCalendar.update({
|
||||
where: { id: existing.id },
|
||||
data: {
|
||||
name: payload.name,
|
||||
timezone: payload.timezone ?? "Asia/Shanghai",
|
||||
legal_entity_id: payload.legalEntityId ?? null,
|
||||
enabled: true,
|
||||
archived_at: null,
|
||||
updated_at: now,
|
||||
},
|
||||
})
|
||||
: await prisma.holidayCalendar.create({
|
||||
data: {
|
||||
code: payload.code,
|
||||
name: payload.name,
|
||||
timezone: payload.timezone ?? "Asia/Shanghai",
|
||||
legal_entity_id: payload.legalEntityId ?? null,
|
||||
enabled: true,
|
||||
archived_at: null,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.holidayCalendarDay.deleteMany({
|
||||
where: { calendar_id: calendar.id },
|
||||
});
|
||||
|
||||
if (payload.days?.length) {
|
||||
await prisma.holidayCalendarDay.createMany({
|
||||
data: payload.days.map((item) => ({
|
||||
calendar_id: calendar.id,
|
||||
date: new Date(`${item.date}T00:00:00.000Z`),
|
||||
day_type: item.dayType,
|
||||
name: item.name,
|
||||
note: item.note ?? null,
|
||||
created_at: now,
|
||||
created_by: null,
|
||||
updated_at: now,
|
||||
updated_by: null,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
if (payload.setAsDefault && payload.legalEntityId) {
|
||||
await prisma.legalEntity.update({
|
||||
where: { id: payload.legalEntityId },
|
||||
data: {
|
||||
default_holiday_calendar_id: calendar.id,
|
||||
updated_at: now,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return calendar;
|
||||
}
|
||||
|
||||
async function ensureEmployee(prisma, payload) {
|
||||
const now = new Date();
|
||||
const passwordHash = await hashPassword(payload.password);
|
||||
@@ -50,6 +191,7 @@ async function ensureEmployee(prisma, payload) {
|
||||
name: payload.name,
|
||||
phone: payload.phone,
|
||||
department_id: payload.departmentId,
|
||||
legal_entity_id: payload.legalEntityId ?? null,
|
||||
title: payload.title,
|
||||
direct_manager_id: payload.directManagerId,
|
||||
hire_date: payload.hireDate,
|
||||
@@ -86,6 +228,7 @@ async function ensureEmployee(prisma, payload) {
|
||||
phone: payload.phone,
|
||||
email: payload.email,
|
||||
department_id: payload.departmentId,
|
||||
legal_entity_id: payload.legalEntityId ?? null,
|
||||
title: payload.title,
|
||||
direct_manager_id: payload.directManagerId,
|
||||
hire_date: payload.hireDate,
|
||||
@@ -141,6 +284,14 @@ async function main() {
|
||||
parent_id: rootDepartment.id,
|
||||
});
|
||||
|
||||
const legalEntity = await ensureLegalEntity(prisma, {
|
||||
code: "HYB_CN",
|
||||
name: "海音达科技有限公司",
|
||||
shortName: "海音达",
|
||||
currencyCode: "CNY",
|
||||
taxNo: null,
|
||||
});
|
||||
|
||||
const ceo = await ensureEmployee(prisma, {
|
||||
employeeNo: process.env.SEED_CEO_EMPLOYEE_NO ?? "E0001",
|
||||
name: "CEO",
|
||||
@@ -148,6 +299,7 @@ async function main() {
|
||||
email: process.env.SEED_CEO_EMAIL ?? "[email protected]",
|
||||
password: process.env.SEED_CEO_PASSWORD ?? "CeoPassword1234",
|
||||
departmentId: rootDepartment.id,
|
||||
legalEntityId: legalEntity.id,
|
||||
title: "总经理",
|
||||
directManagerId: null,
|
||||
roles: ["ceo", "employee"],
|
||||
@@ -161,6 +313,7 @@ async function main() {
|
||||
email: process.env.SEED_ADMIN_EMAIL ?? "[email protected]",
|
||||
password: process.env.SEED_ADMIN_PASSWORD ?? "AdminPassword1234",
|
||||
departmentId: managementDepartment.id,
|
||||
legalEntityId: legalEntity.id,
|
||||
title: "系统管理员",
|
||||
directManagerId: ceo.id,
|
||||
roles: ["admin", "employee"],
|
||||
@@ -174,6 +327,7 @@ async function main() {
|
||||
email: process.env.SEED_MANAGER_EMAIL ?? "[email protected]",
|
||||
password: process.env.SEED_MANAGER_PASSWORD ?? "ManagerPassword1234",
|
||||
departmentId: rdDepartment.id,
|
||||
legalEntityId: legalEntity.id,
|
||||
title: "研发经理",
|
||||
directManagerId: ceo.id,
|
||||
roles: ["manager", "employee"],
|
||||
@@ -187,6 +341,7 @@ async function main() {
|
||||
email: process.env.SEED_EMPLOYEE_EMAIL ?? "[email protected]",
|
||||
password: process.env.SEED_EMPLOYEE_PASSWORD ?? "EmployeePassword1234",
|
||||
departmentId: rdDepartment.id,
|
||||
legalEntityId: legalEntity.id,
|
||||
title: "研发工程师",
|
||||
directManagerId: manager.id,
|
||||
roles: ["employee"],
|
||||
@@ -200,6 +355,7 @@ async function main() {
|
||||
email: process.env.SEED_FINANCE_EMAIL ?? "[email protected]",
|
||||
password: process.env.SEED_FINANCE_PASSWORD ?? "FinancePassword1234",
|
||||
departmentId: financeDepartment.id,
|
||||
legalEntityId: legalEntity.id,
|
||||
title: "财务专员",
|
||||
directManagerId: ceo.id,
|
||||
roles: ["finance", "employee"],
|
||||
@@ -213,6 +369,7 @@ async function main() {
|
||||
email: process.env.SEED_FINANCE_MGR_EMAIL ?? "[email protected]",
|
||||
password: process.env.SEED_FINANCE_MGR_PASSWORD ?? "FinanceMgrPassword1234",
|
||||
departmentId: financeDepartment.id,
|
||||
legalEntityId: legalEntity.id,
|
||||
title: "财务主管",
|
||||
directManagerId: ceo.id,
|
||||
roles: ["finance", "finance_mgr", "employee"],
|
||||
@@ -226,6 +383,7 @@ async function main() {
|
||||
email: process.env.SEED_HR_EMAIL ?? "[email protected]",
|
||||
password: process.env.SEED_HR_PASSWORD ?? "HrPassword1234",
|
||||
departmentId: hrDepartment.id,
|
||||
legalEntityId: legalEntity.id,
|
||||
title: "人事专员",
|
||||
directManagerId: ceo.id,
|
||||
roles: ["hr"],
|
||||
@@ -239,12 +397,51 @@ async function main() {
|
||||
email: process.env.SEED_ADMINISTRATION_EMAIL ?? "[email protected]",
|
||||
password: process.env.SEED_ADMINISTRATION_PASSWORD ?? "AdministrationPassword1234",
|
||||
departmentId: administrationDepartment.id,
|
||||
legalEntityId: legalEntity.id,
|
||||
title: "行政专员",
|
||||
directManagerId: manager.id,
|
||||
roles: ["administration"],
|
||||
hireDate,
|
||||
});
|
||||
|
||||
const holidayCalendar = await ensureHolidayCalendar(prisma, {
|
||||
code: "HYB_CN_STD",
|
||||
name: "海音达默认日历",
|
||||
timezone: "Asia/Shanghai",
|
||||
legalEntityId: legalEntity.id,
|
||||
setAsDefault: true,
|
||||
days: [
|
||||
{ date: "2026-05-04", dayType: "holiday", name: "公司调休假" },
|
||||
{ date: "2026-05-05", dayType: "holiday", name: "公司调休假" },
|
||||
{ date: "2026-05-09", dayType: "adjusted_workday", name: "周末补班" },
|
||||
],
|
||||
});
|
||||
|
||||
await ensureCostCenter(prisma, {
|
||||
legalEntityId: legalEntity.id,
|
||||
code: "MGMT",
|
||||
name: "管理中心",
|
||||
ownerEmployeeId: admin.id,
|
||||
});
|
||||
await ensureCostCenter(prisma, {
|
||||
legalEntityId: legalEntity.id,
|
||||
code: "FIN",
|
||||
name: "财务中心",
|
||||
ownerEmployeeId: financeMgr.id,
|
||||
});
|
||||
await ensureCostCenter(prisma, {
|
||||
legalEntityId: legalEntity.id,
|
||||
code: "HR",
|
||||
name: "人力中心",
|
||||
ownerEmployeeId: hr.id,
|
||||
});
|
||||
await ensureCostCenter(prisma, {
|
||||
legalEntityId: legalEntity.id,
|
||||
code: "RD",
|
||||
name: "研发中心",
|
||||
ownerEmployeeId: manager.id,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log("Seeded dev users:");
|
||||
// eslint-disable-next-line no-console
|
||||
@@ -263,6 +460,10 @@ async function main() {
|
||||
console.log(`- HR: ${hr.email} / ${process.env.SEED_HR_PASSWORD ?? "HrPassword1234"}`);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`- Administration: ${administration.email} / ${process.env.SEED_ADMINISTRATION_PASSWORD ?? "AdministrationPassword1234"}`);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`- Legal Entity: ${legalEntity.code} / ${legalEntity.name}`);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`- HolidayCalendar: ${holidayCalendar.code} / ${holidayCalendar.name}`);
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
await pool.end();
|
||||
|
||||
在新工单中引用
屏蔽一个用户