Files
mrp/server/src/lib/bootstrap.ts

268 lines
7.2 KiB
TypeScript
Raw Normal View History

2026-03-14 14:44:40 -05:00
import { defaultAdminPermissions, permissions, type PermissionKey } from "@mrp/shared";
import { env } from "../config/env.js";
import { prisma } from "./prisma.js";
import { hashPassword } from "./password.js";
import { ensureDataDirectories } from "./storage.js";
const permissionDescriptions: Record<PermissionKey, string> = {
[permissions.adminManage]: "Full administrative access",
[permissions.companyRead]: "View company settings",
[permissions.companyWrite]: "Update company settings",
[permissions.crmRead]: "View CRM records",
[permissions.crmWrite]: "Manage CRM records",
2026-03-14 21:10:35 -05:00
[permissions.inventoryRead]: "View inventory items and BOMs",
[permissions.inventoryWrite]: "Manage inventory items and BOMs",
2026-03-14 14:44:40 -05:00
[permissions.filesRead]: "View attached files",
[permissions.filesWrite]: "Upload and manage attached files",
[permissions.ganttRead]: "View gantt timelines",
[permissions.salesRead]: "View sales data",
2026-03-14 23:03:17 -05:00
[permissions.salesWrite]: "Manage quotes and sales orders",
2026-03-14 14:44:40 -05:00
[permissions.shippingRead]: "View shipping data",
2026-03-14 23:48:27 -05:00
[permissions.shippingWrite]: "Manage shipments",
2026-03-14 14:44:40 -05:00
};
export async function bootstrapAppData() {
await ensureDataDirectories();
for (const permissionKey of defaultAdminPermissions) {
await prisma.permission.upsert({
where: { key: permissionKey },
update: {},
create: {
key: permissionKey,
description: permissionDescriptions[permissionKey],
},
});
}
const adminRole = await prisma.role.upsert({
where: { name: "Administrator" },
update: { description: "Full system access" },
create: {
name: "Administrator",
description: "Full system access",
},
});
const allPermissions = await prisma.permission.findMany({
where: {
key: {
in: defaultAdminPermissions,
},
},
});
for (const permission of allPermissions) {
await prisma.rolePermission.upsert({
where: {
roleId_permissionId: {
roleId: adminRole.id,
permissionId: permission.id,
},
},
update: {},
create: {
roleId: adminRole.id,
permissionId: permission.id,
},
});
}
const adminUser = await prisma.user.upsert({
where: { email: env.ADMIN_EMAIL },
update: {},
create: {
email: env.ADMIN_EMAIL,
firstName: "System",
lastName: "Administrator",
passwordHash: await hashPassword(env.ADMIN_PASSWORD),
},
});
await prisma.userRole.upsert({
where: {
userId_roleId: {
userId: adminUser.id,
roleId: adminRole.id,
},
},
update: {},
create: {
userId: adminUser.id,
roleId: adminRole.id,
},
});
const existingProfile = await prisma.companyProfile.findFirst({
where: { isActive: true },
});
if (!existingProfile) {
await prisma.companyProfile.create({
data: {
companyName: "MRP Codex Manufacturing",
legalName: "MRP Codex Manufacturing LLC",
email: "operations@example.com",
phone: "+1 (555) 010-2000",
website: "https://example.com",
taxId: "99-9999999",
addressLine1: "100 Foundry Lane",
addressLine2: "Suite 200",
city: "Chicago",
state: "IL",
postalCode: "60601",
country: "USA",
},
});
}
if ((await prisma.customer.count()) === 0) {
await prisma.customer.createMany({
data: [
{
name: "Acme Components",
email: "buyer@acme.example",
phone: "555-0101",
addressLine1: "1 Industrial Road",
addressLine2: "",
city: "Detroit",
state: "MI",
postalCode: "48201",
country: "USA",
notes: "Priority account",
},
{
name: "Northwind Fabrication",
email: "ops@northwind.example",
phone: "555-0120",
addressLine1: "42 Assembly Ave",
addressLine2: "",
city: "Milwaukee",
state: "WI",
postalCode: "53202",
country: "USA",
notes: "Requires ASN notice",
},
],
});
}
if ((await prisma.vendor.count()) === 0) {
await prisma.vendor.create({
data: {
name: "SteelSource Supply",
email: "sales@steelsource.example",
phone: "555-0142",
addressLine1: "77 Mill Street",
addressLine2: "",
city: "Gary",
state: "IN",
postalCode: "46402",
country: "USA",
notes: "Lead time 5 business days",
},
});
}
2026-03-14 21:10:35 -05:00
if ((await prisma.inventoryItem.count()) === 0) {
const plate = await prisma.inventoryItem.create({
data: {
sku: "RM-PLATE-AL-125",
name: "Aluminum Plate 1/8in",
description: "Raw aluminum plate stock for fabricated assemblies.",
type: "PURCHASED",
status: "ACTIVE",
unitOfMeasure: "EA",
isSellable: false,
isPurchasable: true,
defaultCost: 42.5,
2026-03-14 23:23:43 -05:00
defaultPrice: null,
2026-03-14 21:10:35 -05:00
notes: "Primary sheet stock for enclosure fabrication.",
},
});
const fastener = await prisma.inventoryItem.create({
data: {
sku: "HW-SCREW-832",
name: "8-32 Socket Head Screw",
description: "Standard socket head cap screw for enclosure assemblies.",
type: "PURCHASED",
status: "ACTIVE",
unitOfMeasure: "EA",
isSellable: false,
isPurchasable: true,
defaultCost: 0.18,
2026-03-14 23:23:43 -05:00
defaultPrice: null,
2026-03-14 21:10:35 -05:00
notes: "Bulk hardware item.",
},
});
const assembly = await prisma.inventoryItem.create({
data: {
sku: "FG-CTRL-BASE",
name: "Control Base Assembly",
description: "Base enclosure assembly for standard control packages.",
type: "ASSEMBLY",
status: "ACTIVE",
unitOfMeasure: "EA",
isSellable: true,
isPurchasable: false,
defaultCost: 118,
2026-03-14 23:23:43 -05:00
defaultPrice: 249,
2026-03-14 21:10:35 -05:00
notes: "Starter BOM for the inventory foundation slice.",
},
});
await prisma.inventoryBomLine.createMany({
data: [
{
parentItemId: assembly.id,
componentItemId: plate.id,
quantity: 2,
unitOfMeasure: "EA",
notes: "Side panel blanks",
position: 10,
},
{
parentItemId: assembly.id,
componentItemId: fastener.id,
quantity: 12,
unitOfMeasure: "EA",
notes: "General assembly hardware",
position: 20,
},
],
});
}
2026-03-14 21:23:22 -05:00
if ((await prisma.warehouse.count()) === 0) {
await prisma.warehouse.create({
data: {
code: "MAIN",
name: "Main Warehouse",
notes: "Primary stocking location for finished goods and purchased materials.",
locations: {
create: [
{
code: "RECV",
name: "Receiving",
notes: "Initial inbound inspection and receipt staging.",
},
{
code: "STOCK-A1",
name: "Aisle A1",
notes: "General rack storage for standard material.",
},
{
code: "FG-STAGE",
name: "Finished Goods Staging",
notes: "Outbound-ready finished assemblies.",
},
],
},
},
});
}
2026-03-14 21:10:35 -05:00
}