251 lines
11 KiB
TypeScript
251 lines
11 KiB
TypeScript
/**
|
|
* Seed script for Dispo v2 reference data:
|
|
* Countries, MetroCities, OrgUnits, UtilizationCategories, Clients, ManagementLevels.
|
|
*
|
|
* Run: npx tsx src/seed-dispo-v2.ts
|
|
*/
|
|
import {
|
|
DISPO_REQUIRED_ROLE_SEEDS,
|
|
DISPO_UTILIZATION_CATEGORIES,
|
|
} from "@capakraken/shared";
|
|
import { PrismaClient } from "@prisma/client";
|
|
import { loadWorkspaceEnv } from "./load-workspace-env.js";
|
|
import { assertSafeSeedTarget } from "./safe-destructive-env.js";
|
|
|
|
loadWorkspaceEnv();
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
async function main() {
|
|
assertSafeSeedTarget("db:seed:dispo-v2");
|
|
console.log("Seeding Dispo v2 reference data...");
|
|
|
|
// ─── Countries + Metro Cities ─────────────────────────────────────────────
|
|
|
|
const countries = [
|
|
{ code: "CR", name: "Costa Rica", dailyWorkingHours: 8, cities: ["Costa Rica"] },
|
|
{ code: "DE", name: "Germany", dailyWorkingHours: 8, cities: ["Augsburg", "Berlin", "Bonn", "Frankfurt", "Hamburg", "Koeln", "Muenchen", "Stuttgart"] },
|
|
{ code: "HU", name: "Hungary", dailyWorkingHours: 8, cities: ["Hungary"] },
|
|
{ code: "IN", name: "India", dailyWorkingHours: 9, cities: ["India"] },
|
|
{ code: "IT", name: "Italy", dailyWorkingHours: 8, cities: ["Italy"] },
|
|
{ code: "PT", name: "Portugal", dailyWorkingHours: 8, cities: ["Lisbon"] },
|
|
{
|
|
code: "ES",
|
|
name: "Spain",
|
|
dailyWorkingHours: 8,
|
|
cities: ["Spain"],
|
|
scheduleRules: {
|
|
type: "spain",
|
|
fridayHours: 6.5,
|
|
summerPeriod: { from: "07-01", to: "09-15" },
|
|
summerHours: 6.5,
|
|
regularHours: 9,
|
|
},
|
|
},
|
|
{ code: "GB", name: "United Kingdom", dailyWorkingHours: 8, cities: ["Birmingham"] },
|
|
] as const;
|
|
|
|
for (const c of countries) {
|
|
const country = await prisma.country.upsert({
|
|
where: { code: c.code },
|
|
update: { name: c.name, dailyWorkingHours: c.dailyWorkingHours },
|
|
create: {
|
|
code: c.code,
|
|
name: c.name,
|
|
dailyWorkingHours: c.dailyWorkingHours,
|
|
...("scheduleRules" in c ? { scheduleRules: c.scheduleRules } : {}),
|
|
},
|
|
});
|
|
for (const cityName of c.cities) {
|
|
await prisma.metroCity.upsert({
|
|
where: { countryId_name: { countryId: country.id, name: cityName } },
|
|
update: {},
|
|
create: { name: cityName, countryId: country.id },
|
|
});
|
|
}
|
|
}
|
|
console.log(" Countries + MetroCities: done");
|
|
|
|
// ─── Org Unit Hierarchy ───────────────────────────────────────────────────
|
|
|
|
const l5 = await prisma.orgUnit.upsert({
|
|
where: { parentId_name: { parentId: null as unknown as string, name: "Content Production" } },
|
|
update: {},
|
|
create: { name: "Content Production", level: 5, sortOrder: 1 },
|
|
}).catch(async () => {
|
|
// parentId_name unique doesn't work with null parentId in all Prisma versions
|
|
const existing = await prisma.orgUnit.findFirst({ where: { name: "Content Production", level: 5, parentId: null } });
|
|
if (existing) return existing;
|
|
return prisma.orgUnit.create({ data: { name: "Content Production", level: 5, sortOrder: 1 } });
|
|
});
|
|
|
|
const l6Data = [
|
|
{ name: "CGI Content", sortOrder: 1 },
|
|
{ name: "CGI Technology", sortOrder: 2 },
|
|
{ name: "Creative Content Production", sortOrder: 3 },
|
|
{ name: "VFX", sortOrder: 4 },
|
|
];
|
|
|
|
const l7Data: Record<string, { name: string; sortOrder: number }[]> = {
|
|
"CGI Content": [
|
|
{ name: "Art Direction", sortOrder: 1 },
|
|
{ name: "Capability Development", sortOrder: 2 },
|
|
{ name: "CGI Production", sortOrder: 3 },
|
|
{ name: "Product Data Management", sortOrder: 4 },
|
|
{ name: "Program/Delivery Mgmt", sortOrder: 5 },
|
|
],
|
|
"CGI Technology": [
|
|
{ name: "CGI Development", sortOrder: 1 },
|
|
{ name: "IT Development", sortOrder: 2 },
|
|
],
|
|
"Creative Content Production": [
|
|
{ name: "Creative Content Production", sortOrder: 1 },
|
|
],
|
|
"VFX": [
|
|
{ name: "2D & Art Direction", sortOrder: 1 },
|
|
{ name: "3D", sortOrder: 2 },
|
|
{ name: "Program/Delivery Mgmt & Other", sortOrder: 3 },
|
|
],
|
|
};
|
|
|
|
for (const l6Item of l6Data) {
|
|
let l6 = await prisma.orgUnit.findFirst({ where: { name: l6Item.name, level: 6, parentId: l5.id } });
|
|
if (!l6) {
|
|
l6 = await prisma.orgUnit.create({
|
|
data: { name: l6Item.name, level: 6, parentId: l5.id, sortOrder: l6Item.sortOrder },
|
|
});
|
|
}
|
|
for (const l7Item of l7Data[l6Item.name] ?? []) {
|
|
const existing = await prisma.orgUnit.findFirst({ where: { name: l7Item.name, level: 7, parentId: l6.id } });
|
|
if (!existing) {
|
|
await prisma.orgUnit.create({
|
|
data: { name: l7Item.name, level: 7, parentId: l6.id, sortOrder: l7Item.sortOrder },
|
|
});
|
|
}
|
|
}
|
|
}
|
|
console.log(" OrgUnits: done");
|
|
|
|
// ─── Utilization Categories ───────────────────────────────────────────────
|
|
|
|
for (const cat of DISPO_UTILIZATION_CATEGORIES) {
|
|
await prisma.utilizationCategory.upsert({
|
|
where: { code: cat.code },
|
|
update: { name: cat.name, description: cat.description, sortOrder: cat.sortOrder },
|
|
create: cat,
|
|
});
|
|
}
|
|
console.log(" UtilizationCategories: done");
|
|
|
|
// ─── Roles ────────────────────────────────────────────────────────────────
|
|
|
|
for (const role of DISPO_REQUIRED_ROLE_SEEDS) {
|
|
await prisma.role.upsert({
|
|
where: { name: role.name },
|
|
update: { description: role.description },
|
|
create: role,
|
|
});
|
|
}
|
|
console.log(" Roles: done");
|
|
|
|
// ─── Management Level Groups + Levels ─────────────────────────────────────
|
|
|
|
const mgmtGroups = [
|
|
{ name: "Accenture Leadership", targetPercentage: 0.365, sortOrder: 1, levels: [] as string[] },
|
|
{ name: "Senior Manager", targetPercentage: 0.546, sortOrder: 2, levels: ["5-Associate Director", "6-Senior Manager"] },
|
|
{ name: "Manager", targetPercentage: 0.747, sortOrder: 3, levels: ["7-Manager"] },
|
|
{ name: "Consultant", targetPercentage: 0.808, sortOrder: 4, levels: ["8-Associate Manager", "9-Team Lead/Consultant"] },
|
|
{ name: "Analyst", targetPercentage: 0.805, sortOrder: 5, levels: ["10-Senior Analyst", "11-Analyst"] },
|
|
{ name: "Associate", targetPercentage: 0.770, sortOrder: 6, levels: ["12-Associate", "13-New Associate"] },
|
|
];
|
|
|
|
for (const grp of mgmtGroups) {
|
|
const group = await prisma.managementLevelGroup.upsert({
|
|
where: { name: grp.name },
|
|
update: { targetPercentage: grp.targetPercentage, sortOrder: grp.sortOrder },
|
|
create: { name: grp.name, targetPercentage: grp.targetPercentage, sortOrder: grp.sortOrder },
|
|
});
|
|
for (const levelName of grp.levels) {
|
|
await prisma.managementLevel.upsert({
|
|
where: { name: levelName },
|
|
update: { groupId: group.id },
|
|
create: { name: levelName, groupId: group.id },
|
|
});
|
|
}
|
|
}
|
|
console.log(" ManagementLevels: done");
|
|
|
|
// ─── Clients (WBS Master + Sub-entities) ──────────────────────────────────
|
|
|
|
const clients: { name: string; code?: string; children: string[] }[] = [
|
|
{ name: "BMW", code: "BMW", children: ["BMW AG"] },
|
|
{ name: "VOLKSWAGEN", code: "VW", children: ["Audi Business Innovation GmbH", "Dr. Ing. h.c. F. Porsche AG", "MAN Truck & Bus SE", "Volkswagen AG"] },
|
|
{ name: "DAIMLER", code: "DAIMLER", children: ["antoni garage GmbH & Co. KG", "Mercedes-Benz AG"] },
|
|
{ name: "EXOR-STELLANTIS", code: "STELLANTIS", children: ["AUTOMOBILES PEUGEOT", "FCA Italy S.p.A.", "Ferrari S.p.A", "MASERATI SPA A SOCIO UNICO"] },
|
|
{ name: "TATA MOTORS GROUP", code: "JLR", children: ["Jaguar Land Rover"] },
|
|
{ name: "SCHWARZ GROUP", code: "LIDL", children: ["Lidl Stiftung & Co. KG"] },
|
|
{ name: "INA-HOLDING SCHAEFFLER GMBH & CO KG", children: ["Schaeffler Technologies"] },
|
|
{ name: "AIRBUS GROUP", children: ["Airbus SAS"] },
|
|
{ name: "ALDI EINKAUF GMBH & CO. OHG", children: ["ALDI Einkauf SE & co. oHG", "ALDI SUED Dienstleistungs-SE & Co.oH"] },
|
|
{ name: "ARLA", children: ["Arla Foods amba"] },
|
|
{ name: "BLANC & FISCHER FAMILIENHOLDING", children: ["BLANCO GmbH + Co KG"] },
|
|
{ name: "BMDS & ITZBUND ASG", children: ["Bundesministerium der Finanzen"] },
|
|
{ name: "BOSCH", children: ["BSH Hausgeraete GmbH"] },
|
|
{ name: "CECONOMY AG", children: ["Media-Saturn-Holding GmbH"] },
|
|
{ name: "COMMERZBANK AG", children: ["COMMERZBANK AG"] },
|
|
{ name: "COOPERATIVE BANKS", children: ["Bank fuer Sozialwirtschaft"] },
|
|
{ name: "HENKEL", children: ["Henkel AG & Co. KGaA"] },
|
|
{ name: "JP MORGAN CHASE & CO", children: ["J.P. Morgan SE"] },
|
|
{ name: "L'OREAL", children: ["L'oreal Singapore Pte Ltd."] },
|
|
{ name: "LUFTHANSA", children: ["Lufthansa Technik AG"] },
|
|
{ name: "LVM", children: ["LVM Landwirtschaftlicher"] },
|
|
{ name: "NESTLE", children: ["Nestec Ltd.", "Societe des Produits Nestle S.A."] },
|
|
{ name: "NETFLIX, INC.", children: ["Bride Lake Productions, LLC", "FOUR FACTOR PRODUCTIONS"] },
|
|
{ name: "NOVARTIS", children: ["Novartis Pharma AG"] },
|
|
{ name: "NOVO NORDISK", children: ["Novo Nordisk Pharma GmbH"] },
|
|
{ name: "OTTO", children: ["bonprix Handelsgesellschaft mbH"] },
|
|
{ name: "PIF & SOVEREIGN FUNDS", children: ["Public Investment Fund"] },
|
|
{ name: "PROSIEBENSAT.1 MEDIA AG", children: ["Seven.One Entertainment Group GmbH"] },
|
|
{ name: "QIDDIYA INVESTMENT COMPANY", children: ["Qiddiyah Investment Company"] },
|
|
{ name: "RED BULL", children: ["RasenBallsport Leipzig GmbH"] },
|
|
{ name: "SIEMENS", children: ["Siemens AG"] },
|
|
{ name: "THUEGA", children: ["TAP Steuerungsgesellschaft"] },
|
|
{ name: "UNICREDIT GROUP", children: ["UniCredit Bank GmbH"] },
|
|
{ name: "WARNER BROS DISCOVERY", children: ["RANDOM PRODUCTIONS LLC"] },
|
|
{ name: "WESTLOTTO", children: ["Westdeutsche Lotterie GmbH & Co. OH"] },
|
|
];
|
|
|
|
for (let i = 0; i < clients.length; i++) {
|
|
const c = clients[i]!;
|
|
// Find or create master client
|
|
let master = await prisma.client.findFirst({ where: { name: c.name, parentId: null } });
|
|
if (!master) {
|
|
master = await prisma.client.create({
|
|
data: {
|
|
name: c.name,
|
|
...(c.code ? { code: c.code } : {}),
|
|
sortOrder: i + 1,
|
|
},
|
|
});
|
|
}
|
|
for (const childName of c.children) {
|
|
const existing = await prisma.client.findFirst({ where: { name: childName, parentId: master.id } });
|
|
if (!existing) {
|
|
await prisma.client.create({
|
|
data: { name: childName, parentId: master.id },
|
|
});
|
|
}
|
|
}
|
|
}
|
|
console.log(" Clients: done");
|
|
|
|
console.log("Dispo v2 seed complete.");
|
|
}
|
|
|
|
main()
|
|
.catch((e) => {
|
|
console.error(e);
|
|
process.exit(1);
|
|
})
|
|
.finally(() => prisma.$disconnect());
|