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:
parent
36f3826684
commit
535200aca2
8 changed files with 788 additions and 0 deletions
16
App/pelagia-portal/lib/db.ts
Normal file
16
App/pelagia-portal/lib/db.ts
Normal 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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
-- AlterEnum
|
||||
ALTER TYPE "ActionType" ADD VALUE 'MANAGER_LINE_EDIT';
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "POLineItem" ADD COLUMN "size" TEXT;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
3
App/pelagia-portal/prisma/migrations/migration_lock.toml
Normal file
3
App/pelagia-portal/prisma/migrations/migration_lock.toml
Normal 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"
|
||||
226
App/pelagia-portal/prisma/schema.prisma
Normal file
226
App/pelagia-portal/prisma/schema.prisma
Normal 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])
|
||||
}
|
||||
280
App/pelagia-portal/prisma/seed.ts
Normal file
280
App/pelagia-portal/prisma/seed.ts
Normal 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());
|
||||
Loading…
Add table
Reference in a new issue