feat(P25-1/P25-5/P26-1/P26-3/P24-1/P29-4): 落地内部OA够用版后端与试运行能力
关联 Issue: P25-1, P25-2, P25-3, P25-4, P25-5, P26-1, P26-3, P24-1, P24-2, P29-1, P29-2, P29-3, P29-4
这个提交包含在:
@@ -233,6 +233,70 @@ const DEFAULT_FEATURE_FLAGS = [
|
||||
department_ids: [],
|
||||
note: "bootstrap default: force_off",
|
||||
},
|
||||
{
|
||||
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",
|
||||
},
|
||||
];
|
||||
|
||||
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 = [
|
||||
@@ -411,6 +475,51 @@ async function seedFeatureFlags(prisma) {
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@@ -608,6 +717,7 @@ async function main() {
|
||||
await seedWorkflowDefinition(prisma);
|
||||
await seedLeaveTypes(prisma);
|
||||
await seedFeatureFlags(prisma);
|
||||
await seedBudgetControlRules(prisma);
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log("Seeded config data:");
|
||||
|
||||
@@ -109,6 +109,46 @@ async function ensureCostCenter(prisma, payload) {
|
||||
});
|
||||
}
|
||||
|
||||
async function ensureBudgetPool(prisma, payload) {
|
||||
const now = new Date();
|
||||
const existing = await prisma.budgetPool.findUnique({
|
||||
where: { code: payload.code },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
const data = {
|
||||
name: payload.name,
|
||||
department_id: payload.departmentId ?? null,
|
||||
legal_entity_id: payload.legalEntityId ?? null,
|
||||
cost_center_id: payload.costCenterId ?? null,
|
||||
status: "active",
|
||||
period_start: payload.periodStart,
|
||||
period_end: payload.periodEnd,
|
||||
allocated_amount: payload.allocatedAmount,
|
||||
warning_threshold_percent: payload.warningThresholdPercent ?? 80,
|
||||
archived_at: null,
|
||||
updated_at: now,
|
||||
updated_by: null,
|
||||
};
|
||||
|
||||
if (existing) {
|
||||
return prisma.budgetPool.update({
|
||||
where: { id: existing.id },
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
return prisma.budgetPool.create({
|
||||
data: {
|
||||
code: payload.code,
|
||||
used_amount: payload.usedAmount ?? "0.00",
|
||||
created_at: now,
|
||||
created_by: null,
|
||||
...data,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function ensureSupplier(prisma, payload) {
|
||||
const now = new Date();
|
||||
const existing = await prisma.supplier.findUnique({
|
||||
@@ -303,6 +343,9 @@ async function main() {
|
||||
await prisma.$connect();
|
||||
|
||||
const hireDate = new Date("2020-01-01");
|
||||
const currentYear = new Date().getUTCFullYear();
|
||||
const fiscalPeriodStart = new Date(`${currentYear}-01-01T00:00:00.000Z`);
|
||||
const fiscalPeriodEnd = new Date(`${currentYear}-12-31T00:00:00.000Z`);
|
||||
|
||||
try {
|
||||
const rootDepartment = await ensureDepartment(prisma, { name: "公司" });
|
||||
@@ -460,7 +503,7 @@ async function main() {
|
||||
],
|
||||
});
|
||||
|
||||
await ensureCostCenter(prisma, {
|
||||
const managementCostCenter = await ensureCostCenter(prisma, {
|
||||
legalEntityId: legalEntity.id,
|
||||
code: "MGMT",
|
||||
name: "管理中心",
|
||||
@@ -478,7 +521,7 @@ async function main() {
|
||||
name: "人力中心",
|
||||
ownerEmployeeId: hr.id,
|
||||
});
|
||||
await ensureCostCenter(prisma, {
|
||||
const rdCostCenter = await ensureCostCenter(prisma, {
|
||||
legalEntityId: legalEntity.id,
|
||||
code: "RD",
|
||||
name: "研发中心",
|
||||
@@ -496,6 +539,27 @@ async function main() {
|
||||
legalEntityId: legalEntity.id,
|
||||
});
|
||||
|
||||
const managementBudgetPool = await ensureBudgetPool(prisma, {
|
||||
code: `DEV_BUDGET_MGMT_${currentYear}`,
|
||||
name: `${currentYear} 管理中心预算池`,
|
||||
legalEntityId: legalEntity.id,
|
||||
costCenterId: managementCostCenter.id,
|
||||
periodStart: fiscalPeriodStart,
|
||||
periodEnd: fiscalPeriodEnd,
|
||||
allocatedAmount: "500000.00",
|
||||
warningThresholdPercent: 80,
|
||||
});
|
||||
|
||||
const rdBudgetPool = await ensureBudgetPool(prisma, {
|
||||
code: `DEV_BUDGET_RD_${currentYear}`,
|
||||
name: `${currentYear} 研发部预算池`,
|
||||
departmentId: rdDepartment.id,
|
||||
periodStart: fiscalPeriodStart,
|
||||
periodEnd: fiscalPeriodEnd,
|
||||
allocatedAmount: "800000.00",
|
||||
warningThresholdPercent: 75,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log("Seeded dev users:");
|
||||
// eslint-disable-next-line no-console
|
||||
@@ -520,6 +584,8 @@ async function main() {
|
||||
console.log(`- HolidayCalendar: ${holidayCalendar.code} / ${holidayCalendar.name}`);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`- Supplier: ${supplier.code} / ${supplier.name}`);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`- Budget Pools: ${managementBudgetPool.code}, ${rdBudgetPool.code}`);
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
await pool.end();
|
||||
|
||||
在新工单中引用
屏蔽一个用户