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