feat(db): Prisma schema with all 11 models, migrations and seed data

Models: User, Vessel, Account, Vendor, Product, PurchaseOrder, POLineItem,
PODocument, POAction, Receipt, Notification.
PO fields include piQuotationNo/Date, requisitionNo/Date, placeOfDelivery,
structured T&C (tcDelivery/tcDispatch/tcInspection/tcTransitInsurance/
tcPaymentTerms/tcOthers), currency default INR.
POLineItem includes gstRate (default 0.18).
Vendor includes address, gstin, contactMobile.
Seed: 5 users across all roles, 3 vessels, 3 accounts, 3 vendors, 3 POs, 4 products.
This commit is contained in:
Hardik 2026-05-05 23:24:04 +05:30
parent 36f3826684
commit 535200aca2
8 changed files with 788 additions and 0 deletions

View file

@ -0,0 +1,16 @@
import { PrismaClient } from "@prisma/client";
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
export const db =
globalForPrisma.prisma ??
new PrismaClient({
log:
process.env.NODE_ENV === "development"
? ["query", "error", "warn"]
: ["error"],
});
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = db;

View file

@ -0,0 +1,228 @@
-- CreateEnum
CREATE TYPE "Role" AS ENUM ('TECHNICAL', 'MANNING', 'ACCOUNTS', 'MANAGER', 'SUPERUSER', 'AUDITOR', 'ADMIN');
-- CreateEnum
CREATE TYPE "POStatus" AS ENUM ('DRAFT', 'SUBMITTED', 'MGR_REVIEW', 'VENDOR_ID_PENDING', 'EDITS_REQUESTED', 'REJECTED', 'MGR_APPROVED', 'SENT_FOR_PAYMENT', 'PAID_DELIVERED', 'CLOSED');
-- CreateEnum
CREATE TYPE "ActionType" AS ENUM ('CREATED', 'SUBMITTED', 'APPROVED', 'APPROVED_WITH_NOTE', 'REJECTED', 'EDITS_REQUESTED', 'VENDOR_ID_REQUESTED', 'VENDOR_ID_PROVIDED', 'PAYMENT_SENT', 'RECEIPT_CONFIRMED', 'CLOSED', 'REASSIGNED', 'PRODUCT_PRICE_UPDATED');
-- CreateTable
CREATE TABLE "User" (
"id" TEXT NOT NULL,
"employeeId" TEXT NOT NULL,
"email" TEXT NOT NULL,
"name" TEXT NOT NULL,
"passwordHash" TEXT NOT NULL,
"role" "Role" NOT NULL,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Vessel" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"imoNumber" TEXT,
"isActive" BOOLEAN NOT NULL DEFAULT true,
CONSTRAINT "Vessel_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Account" (
"id" TEXT NOT NULL,
"code" TEXT NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT,
"isActive" BOOLEAN NOT NULL DEFAULT true,
CONSTRAINT "Account_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Vendor" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"vendorId" TEXT,
"contactName" TEXT,
"contactEmail" TEXT,
"isVerified" BOOLEAN NOT NULL DEFAULT false,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Vendor_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Product" (
"id" TEXT NOT NULL,
"code" TEXT NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT,
"lastPrice" DECIMAL(12,2),
"lastVendorId" TEXT,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"updatedAt" TIMESTAMP(3) NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Product_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "PurchaseOrder" (
"id" TEXT NOT NULL,
"poNumber" TEXT NOT NULL,
"title" TEXT NOT NULL,
"status" "POStatus" NOT NULL DEFAULT 'DRAFT',
"totalAmount" DECIMAL(12,2) NOT NULL,
"currency" TEXT NOT NULL DEFAULT 'USD',
"dateRequired" TIMESTAMP(3),
"projectCode" TEXT,
"managerNote" TEXT,
"paymentRef" TEXT,
"submittedAt" TIMESTAMP(3),
"approvedAt" TIMESTAMP(3),
"paidAt" TIMESTAMP(3),
"closedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"submitterId" TEXT NOT NULL,
"vesselId" TEXT NOT NULL,
"accountId" TEXT NOT NULL,
"vendorId" TEXT,
CONSTRAINT "PurchaseOrder_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "POLineItem" (
"id" TEXT NOT NULL,
"description" TEXT NOT NULL,
"quantity" DECIMAL(10,3) NOT NULL,
"unit" TEXT NOT NULL,
"unitPrice" DECIMAL(12,2) NOT NULL,
"totalPrice" DECIMAL(12,2) NOT NULL,
"sortOrder" INTEGER NOT NULL DEFAULT 0,
"productId" TEXT,
"poId" TEXT NOT NULL,
CONSTRAINT "POLineItem_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "PODocument" (
"id" TEXT NOT NULL,
"fileName" TEXT NOT NULL,
"fileSize" INTEGER NOT NULL,
"mimeType" TEXT NOT NULL,
"storageKey" TEXT NOT NULL,
"uploadedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"poId" TEXT NOT NULL,
CONSTRAINT "PODocument_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "POAction" (
"id" TEXT NOT NULL,
"actionType" "ActionType" NOT NULL,
"note" TEXT,
"metadata" JSONB,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"poId" TEXT NOT NULL,
"actorId" TEXT NOT NULL,
CONSTRAINT "POAction_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Receipt" (
"id" TEXT NOT NULL,
"storageKey" TEXT NOT NULL,
"fileName" TEXT NOT NULL,
"notes" TEXT,
"confirmedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"poId" TEXT NOT NULL,
CONSTRAINT "Receipt_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Notification" (
"id" TEXT NOT NULL,
"subject" TEXT NOT NULL,
"body" TEXT NOT NULL,
"sentAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"status" TEXT NOT NULL DEFAULT 'sent',
"poId" TEXT,
"userId" TEXT NOT NULL,
CONSTRAINT "Notification_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "User_employeeId_key" ON "User"("employeeId");
-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
-- CreateIndex
CREATE UNIQUE INDEX "Vessel_imoNumber_key" ON "Vessel"("imoNumber");
-- CreateIndex
CREATE UNIQUE INDEX "Account_code_key" ON "Account"("code");
-- CreateIndex
CREATE UNIQUE INDEX "Vendor_vendorId_key" ON "Vendor"("vendorId");
-- CreateIndex
CREATE UNIQUE INDEX "Product_code_key" ON "Product"("code");
-- CreateIndex
CREATE UNIQUE INDEX "PurchaseOrder_poNumber_key" ON "PurchaseOrder"("poNumber");
-- CreateIndex
CREATE UNIQUE INDEX "Receipt_poId_key" ON "Receipt"("poId");
-- AddForeignKey
ALTER TABLE "Product" ADD CONSTRAINT "Product_lastVendorId_fkey" FOREIGN KEY ("lastVendorId") REFERENCES "Vendor"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "PurchaseOrder" ADD CONSTRAINT "PurchaseOrder_submitterId_fkey" FOREIGN KEY ("submitterId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "PurchaseOrder" ADD CONSTRAINT "PurchaseOrder_vesselId_fkey" FOREIGN KEY ("vesselId") REFERENCES "Vessel"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "PurchaseOrder" ADD CONSTRAINT "PurchaseOrder_accountId_fkey" FOREIGN KEY ("accountId") REFERENCES "Account"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "PurchaseOrder" ADD CONSTRAINT "PurchaseOrder_vendorId_fkey" FOREIGN KEY ("vendorId") REFERENCES "Vendor"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "POLineItem" ADD CONSTRAINT "POLineItem_productId_fkey" FOREIGN KEY ("productId") REFERENCES "Product"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "POLineItem" ADD CONSTRAINT "POLineItem_poId_fkey" FOREIGN KEY ("poId") REFERENCES "PurchaseOrder"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "PODocument" ADD CONSTRAINT "PODocument_poId_fkey" FOREIGN KEY ("poId") REFERENCES "PurchaseOrder"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "POAction" ADD CONSTRAINT "POAction_poId_fkey" FOREIGN KEY ("poId") REFERENCES "PurchaseOrder"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "POAction" ADD CONSTRAINT "POAction_actorId_fkey" FOREIGN KEY ("actorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Receipt" ADD CONSTRAINT "Receipt_poId_fkey" FOREIGN KEY ("poId") REFERENCES "PurchaseOrder"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Notification" ADD CONSTRAINT "Notification_poId_fkey" FOREIGN KEY ("poId") REFERENCES "PurchaseOrder"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Notification" ADD CONSTRAINT "Notification_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View file

@ -0,0 +1,5 @@
-- AlterEnum
ALTER TYPE "ActionType" ADD VALUE 'MANAGER_LINE_EDIT';
-- AlterTable
ALTER TABLE "POLineItem" ADD COLUMN "size" TEXT;

View file

@ -0,0 +1,16 @@
-- AlterTable
ALTER TABLE "POLineItem" ADD COLUMN "gstRate" DECIMAL(5,4) NOT NULL DEFAULT 0.18;
-- AlterTable
ALTER TABLE "PurchaseOrder" ADD COLUMN "piQuotationDate" TIMESTAMP(3),
ADD COLUMN "piQuotationNo" TEXT,
ADD COLUMN "placeOfDelivery" TEXT,
ADD COLUMN "requisitionDate" TIMESTAMP(3),
ADD COLUMN "requisitionNo" TEXT,
ADD COLUMN "termsAndConditions" TEXT,
ALTER COLUMN "currency" SET DEFAULT 'INR';
-- AlterTable
ALTER TABLE "Vendor" ADD COLUMN "address" TEXT,
ADD COLUMN "contactMobile" TEXT,
ADD COLUMN "gstin" TEXT;

View file

@ -0,0 +1,14 @@
/*
Warnings:
- You are about to drop the column `termsAndConditions` on the `PurchaseOrder` table. All the data in the column will be lost.
*/
-- AlterTable
ALTER TABLE "PurchaseOrder" DROP COLUMN "termsAndConditions",
ADD COLUMN "tcDelivery" TEXT,
ADD COLUMN "tcDispatch" TEXT,
ADD COLUMN "tcInspection" TEXT,
ADD COLUMN "tcOthers" TEXT,
ADD COLUMN "tcPaymentTerms" TEXT,
ADD COLUMN "tcTransitInsurance" TEXT;

View file

@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "postgresql"

View file

@ -0,0 +1,226 @@
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
enum Role {
TECHNICAL
MANNING
ACCOUNTS
MANAGER
SUPERUSER
AUDITOR
ADMIN
}
enum POStatus {
DRAFT
SUBMITTED
MGR_REVIEW
VENDOR_ID_PENDING
EDITS_REQUESTED
REJECTED
MGR_APPROVED
SENT_FOR_PAYMENT
PAID_DELIVERED
CLOSED
}
enum ActionType {
CREATED
SUBMITTED
APPROVED
APPROVED_WITH_NOTE
REJECTED
EDITS_REQUESTED
VENDOR_ID_REQUESTED
VENDOR_ID_PROVIDED
PAYMENT_SENT
RECEIPT_CONFIRMED
CLOSED
REASSIGNED
PRODUCT_PRICE_UPDATED
MANAGER_LINE_EDIT
}
model User {
id String @id @default(cuid())
employeeId String @unique
email String @unique
name String
passwordHash String
role Role
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
submittedPOs PurchaseOrder[] @relation("Submitter")
actions POAction[]
notifications Notification[]
}
model Vessel {
id String @id @default(cuid())
name String
imoNumber String? @unique
isActive Boolean @default(true)
purchaseOrders PurchaseOrder[]
}
model Account {
id String @id @default(cuid())
code String @unique
name String
description String?
isActive Boolean @default(true)
purchaseOrders PurchaseOrder[]
}
model Vendor {
id String @id @default(cuid())
name String
vendorId String? @unique
address String?
gstin String?
contactName String?
contactMobile String?
contactEmail String?
isVerified Boolean @default(false)
isActive Boolean @default(true)
createdAt DateTime @default(now())
purchaseOrders PurchaseOrder[]
products Product[] @relation("ProductLastVendor")
}
model Product {
id String @id @default(cuid())
code String @unique
name String
description String?
lastPrice Decimal? @db.Decimal(12, 2)
lastVendorId String?
lastVendor Vendor? @relation("ProductLastVendor", fields: [lastVendorId], references: [id])
isActive Boolean @default(true)
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
lineItems POLineItem[]
}
model PurchaseOrder {
id String @id @default(cuid())
poNumber String @unique
title String
status POStatus @default(DRAFT)
totalAmount Decimal @db.Decimal(12, 2)
currency String @default("INR")
dateRequired DateTime?
projectCode String?
managerNote String?
paymentRef String?
piQuotationNo String?
piQuotationDate DateTime?
requisitionNo String?
requisitionDate DateTime?
placeOfDelivery String?
tcDelivery String?
tcDispatch String?
tcInspection String?
tcTransitInsurance String?
tcPaymentTerms String?
tcOthers String?
submittedAt DateTime?
approvedAt DateTime?
paidAt DateTime?
closedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
submitterId String
submitter User @relation("Submitter", fields: [submitterId], references: [id])
vesselId String
vessel Vessel @relation(fields: [vesselId], references: [id])
accountId String
account Account @relation(fields: [accountId], references: [id])
vendorId String?
vendor Vendor? @relation(fields: [vendorId], references: [id])
lineItems POLineItem[]
documents PODocument[]
actions POAction[]
receipt Receipt?
notifications Notification[]
}
model POLineItem {
id String @id @default(cuid())
description String
quantity Decimal @db.Decimal(10, 3)
unit String
unitPrice Decimal @db.Decimal(12, 2)
totalPrice Decimal @db.Decimal(12, 2)
gstRate Decimal @default(0.18) @db.Decimal(5, 4)
sortOrder Int @default(0)
size String?
productId String?
product Product? @relation(fields: [productId], references: [id])
poId String
po PurchaseOrder @relation(fields: [poId], references: [id], onDelete: Cascade)
}
model PODocument {
id String @id @default(cuid())
fileName String
fileSize Int
mimeType String
storageKey String
uploadedAt DateTime @default(now())
poId String
po PurchaseOrder @relation(fields: [poId], references: [id], onDelete: Cascade)
}
model POAction {
id String @id @default(cuid())
actionType ActionType
note String?
metadata Json?
createdAt DateTime @default(now())
poId String
po PurchaseOrder @relation(fields: [poId], references: [id])
actorId String
actor User @relation(fields: [actorId], references: [id])
}
model Receipt {
id String @id @default(cuid())
storageKey String
fileName String
notes String?
confirmedAt DateTime @default(now())
poId String @unique
po PurchaseOrder @relation(fields: [poId], references: [id])
}
model Notification {
id String @id @default(cuid())
subject String
body String
sentAt DateTime @default(now())
status String @default("sent")
poId String?
po PurchaseOrder? @relation(fields: [poId], references: [id])
userId String
user User @relation(fields: [userId], references: [id])
}

View file

@ -0,0 +1,280 @@
import { PrismaClient, Role } from "@prisma/client";
import bcrypt from "bcryptjs";
const db = new PrismaClient();
async function main() {
console.log("Seeding database...");
const hash = (p: string) => bcrypt.hash(p, 12);
// Users
const admin = await db.user.upsert({
where: { email: "admin@pelagia.local" },
update: {},
create: {
employeeId: "EMP-001",
email: "admin@pelagia.local",
name: "System Admin",
passwordHash: await hash("admin1234"),
role: Role.ADMIN,
},
});
const manager = await db.user.upsert({
where: { email: "manager@pelagia.local" },
update: {},
create: {
employeeId: "EMP-002",
email: "manager@pelagia.local",
name: "James Hartwell",
passwordHash: await hash("manager1234"),
role: Role.MANAGER,
},
});
const technical = await db.user.upsert({
where: { email: "tech@pelagia.local" },
update: {},
create: {
employeeId: "EMP-003",
email: "tech@pelagia.local",
name: "Maria Santos",
passwordHash: await hash("tech1234"),
role: Role.TECHNICAL,
},
});
const accounts = await db.user.upsert({
where: { email: "accounts@pelagia.local" },
update: {},
create: {
employeeId: "EMP-004",
email: "accounts@pelagia.local",
name: "Chen Wei",
passwordHash: await hash("accounts1234"),
role: Role.ACCOUNTS,
},
});
await db.user.upsert({
where: { email: "manning@pelagia.local" },
update: {},
create: {
employeeId: "EMP-005",
email: "manning@pelagia.local",
name: "Raj Patel",
passwordHash: await hash("manning1234"),
role: Role.MANNING,
},
});
// Vessels
const mv1 = await db.vessel.upsert({
where: { imoNumber: "IMO9876543" },
update: {},
create: { name: "MV Pelagia Star", imoNumber: "IMO9876543" },
});
const mv2 = await db.vessel.upsert({
where: { imoNumber: "IMO9123456" },
update: {},
create: { name: "MV Aegean Wind", imoNumber: "IMO9123456" },
});
await db.vessel.upsert({
where: { imoNumber: "IMO9654321" },
update: {},
create: { name: "MV Poseidon", imoNumber: "IMO9654321" },
});
// Accounts
const acc1 = await db.account.upsert({
where: { code: "TECH-OPS" },
update: {},
create: { code: "TECH-OPS", name: "Technical Operations", description: "Engine and deck equipment" },
});
const acc2 = await db.account.upsert({
where: { code: "CREW-MGT" },
update: {},
create: { code: "CREW-MGT", name: "Crew Management", description: "Manning and crew welfare" },
});
await db.account.upsert({
where: { code: "FUEL-BNK" },
update: {},
create: { code: "FUEL-BNK", name: "Fuel & Bunkers", description: "Fuel procurement" },
});
// Vendors
const vendor1 = await db.vendor.upsert({
where: { vendorId: "VND-0001" },
update: {},
create: {
name: "Marine Parts International",
vendorId: "VND-0001",
contactName: "Tony Nguyen",
contactEmail: "tnguyen@marinepartsinternational.com",
isVerified: true,
},
});
await db.vendor.upsert({
where: { vendorId: "VND-0002" },
update: {},
create: {
name: "Global Crew Supplies",
vendorId: "VND-0002",
contactName: "Sarah Kim",
contactEmail: "sarah@globalcrewsupplies.com",
isVerified: true,
},
});
await db.vendor.upsert({
where: { vendorId: "VND-0003" },
update: {},
create: {
name: "Atlas Ship Chandlers",
vendorId: "VND-0003",
contactName: "Marco Rossi",
contactEmail: "marco@atlaschandlers.com",
isVerified: false,
},
});
// Products
const prod1 = await db.product.upsert({
where: { code: "PART-TURBO-SEAL" },
update: {},
create: {
code: "PART-TURBO-SEAL",
name: "Turbocharger Seal Kit",
description: "Replacement seal kit for main engine turbocharger",
},
});
const prod2 = await db.product.upsert({
where: { code: "PART-FP-PUMP" },
update: {},
create: {
code: "PART-FP-PUMP",
name: "High-Pressure Fuel Pump",
description: "Main engine high-pressure fuel pump assembly",
},
});
await db.product.upsert({
where: { code: "SAFE-LIFEJKT" },
update: {},
create: {
code: "SAFE-LIFEJKT",
name: "Life Jacket (SOLAS)",
description: "SOLAS-approved adult life jacket",
},
});
await db.product.upsert({
where: { code: "SAFE-EXTG-9KG" },
update: {},
create: {
code: "SAFE-EXTG-9KG",
name: "Fire Extinguisher 9kg",
description: "Dry powder fire extinguisher, 9kg",
},
});
// Sample POs
const po1 = await db.purchaseOrder.create({
data: {
poNumber: "PO-2026-00001",
title: "Engine Room Spare Parts — MV Pelagia Star",
status: "MGR_REVIEW",
totalAmount: 8450.0,
currency: "USD",
submittedAt: new Date(),
submitterId: technical.id,
vesselId: mv1.id,
accountId: acc1.id,
vendorId: vendor1.id,
lineItems: {
create: [
{ description: "Turbocharger seal kit", quantity: 2, unit: "set", unitPrice: 1200, totalPrice: 2400, sortOrder: 0, productId: prod1.id },
{ description: "High-pressure fuel pump", quantity: 1, unit: "pc", unitPrice: 4800, totalPrice: 4800, sortOrder: 1, productId: prod2.id },
{ description: "O-ring assortment pack", quantity: 5, unit: "pk", unitPrice: 250, totalPrice: 1250, sortOrder: 2 },
],
},
actions: {
create: [
{ actionType: "CREATED", actorId: technical.id },
{ actionType: "SUBMITTED", actorId: technical.id },
],
},
},
});
await db.purchaseOrder.create({
data: {
poNumber: "PO-2026-00002",
title: "Crew Safety Equipment — MV Aegean Wind",
status: "DRAFT",
totalAmount: 3200.0,
currency: "USD",
submitterId: technical.id,
vesselId: mv2.id,
accountId: acc2.id,
lineItems: {
create: [
{ description: "Life jackets (SOLAS)", quantity: 20, unit: "pc", unitPrice: 120, totalPrice: 2400, sortOrder: 0 },
{ description: "Fire extinguisher — 9kg", quantity: 4, unit: "pc", unitPrice: 200, totalPrice: 800, sortOrder: 1 },
],
},
actions: {
create: [{ actionType: "CREATED", actorId: technical.id }],
},
},
});
await db.purchaseOrder.create({
data: {
poNumber: "PO-2026-00003",
title: "Navigation Charts Update — Fleet",
status: "MGR_APPROVED",
totalAmount: 950.0,
currency: "USD",
submittedAt: new Date(Date.now() - 5 * 86400000),
approvedAt: new Date(Date.now() - 2 * 86400000),
submitterId: technical.id,
vesselId: mv1.id,
accountId: acc1.id,
lineItems: {
create: [
{ description: "INT chart folio update", quantity: 1, unit: "set", unitPrice: 950, totalPrice: 950, sortOrder: 0 },
],
},
actions: {
create: [
{ actionType: "CREATED", actorId: technical.id },
{ actionType: "SUBMITTED", actorId: technical.id },
{ actionType: "APPROVED", actorId: manager.id, note: "Approved — update due before next voyage." },
],
},
},
});
console.log("Seed complete.");
console.log("\nDev login credentials:");
console.log(" Admin: admin@pelagia.local / admin1234");
console.log(" Manager: manager@pelagia.local / manager1234");
console.log(" Tech: tech@pelagia.local / tech1234");
console.log(" Accounts: accounts@pelagia.local / accounts1234");
console.log(" Manning: manning@pelagia.local / manning1234");
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(() => db.$disconnect());