From 8e57381b06344081401da1a693a6b54c400d083e Mon Sep 17 00:00:00 2001 From: Hardik Date: Fri, 15 May 2026 12:12:22 +0530 Subject: [PATCH] chore(seed): rewrite for current schema with 10+ entries per table - Vendors: remove deprecated contactName/Email/Mobile fields; use VendorContact nested creates with primary flag, role, mobile, email - Add pincode + lat/lng to all vendors for distance calculations - Sites: 12 Indian port locations with lat/lng - Vessels: 11, linked to sites - Accounts: 12 covering all spend categories - Users: 12 across all roles - ProductVendorPrice: 51 entries linking vendors to products - ItemInventory: 15 stock records across sites - ItemConsumption: 14 daily consumption records - PurchaseOrders: 12 spanning full status lifecycle - Seed is idempotent (all upserts, safe to re-run) Co-Authored-By: Claude Sonnet 4.6 --- App/pelagia-portal/prisma/seed.ts | 1198 +++++++++++++++++++++-------- 1 file changed, 889 insertions(+), 309 deletions(-) diff --git a/App/pelagia-portal/prisma/seed.ts b/App/pelagia-portal/prisma/seed.ts index ee512d2..fb37382 100644 --- a/App/pelagia-portal/prisma/seed.ts +++ b/App/pelagia-portal/prisma/seed.ts @@ -3,583 +3,875 @@ import bcrypt from "bcryptjs"; const db = new PrismaClient(); +const hash = (p: string) => bcrypt.hash(p, 12); + +const d = (daysAgo: number) => new Date(Date.now() - daysAgo * 86_400_000); + async function main() { - console.log("Seeding database..."); + console.log("Seeding database…"); - const hash = (p: string) => bcrypt.hash(p, 12); - - // Users + // ─── 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, - }, + 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, - }, + create: { employeeId: "EMP-002", email: "manager@pelagia.local", name: "James Hartwell", passwordHash: await hash("manager1234"), role: Role.MANAGER }, + }); + + const manager2 = await db.user.upsert({ + where: { email: "manager2@pelagia.local" }, + update: {}, + create: { employeeId: "EMP-011", email: "manager2@pelagia.local", name: "Lakshmi Iyer", 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, - }, + create: { employeeId: "EMP-003", email: "tech@pelagia.local", name: "Maria Santos", passwordHash: await hash("tech1234"), role: Role.TECHNICAL }, + }); + + const tech2 = await db.user.upsert({ + where: { email: "tech2@pelagia.local" }, + update: {}, + create: { employeeId: "EMP-006", email: "tech2@pelagia.local", name: "Arjun Sharma", passwordHash: await hash("tech1234"), role: Role.TECHNICAL }, + }); + + const tech3 = await db.user.upsert({ + where: { email: "tech3@pelagia.local" }, + update: {}, + create: { employeeId: "EMP-010", email: "tech3@pelagia.local", name: "David Chen", 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, - }, + create: { employeeId: "EMP-004", email: "accounts@pelagia.local", name: "Chen Wei", passwordHash: await hash("accounts1234"), role: Role.ACCOUNTS }, + }); + + const accounts2 = await db.user.upsert({ + where: { email: "accounts2@pelagia.local" }, + update: {}, + create: { employeeId: "EMP-012", email: "accounts2@pelagia.local", name: "Anita Bose", passwordHash: await hash("accounts1234"), role: Role.ACCOUNTS }, + }); + + const manning = 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 }, + }); + + const manning2 = await db.user.upsert({ + where: { email: "manning2@pelagia.local" }, + update: {}, + create: { employeeId: "EMP-007", email: "manning2@pelagia.local", name: "Fatima Al-Hassan", passwordHash: await hash("manning1234"), role: Role.MANNING }, + }); + + const superuser = await db.user.upsert({ + where: { email: "superuser@pelagia.local" }, + update: {}, + create: { employeeId: "EMP-008", email: "superuser@pelagia.local", name: "Vikram Menon", passwordHash: await hash("super1234"), role: Role.SUPERUSER }, }); await db.user.upsert({ - where: { email: "manning@pelagia.local" }, + where: { email: "auditor@pelagia.local" }, update: {}, - create: { - employeeId: "EMP-005", - email: "manning@pelagia.local", - name: "Raj Patel", - passwordHash: await hash("manning1234"), - role: Role.MANNING, - }, + create: { employeeId: "EMP-009", email: "auditor@pelagia.local", name: "Priya Krishnan", passwordHash: await hash("audit1234"), role: Role.AUDITOR }, }); - // Vessels - const mv1 = await db.vessel.upsert({ + // ─── Sites ─────────────────────────────────────────────────────────────────── + const siteBOM = await db.site.upsert({ + where: { code: "BOM" }, + update: {}, + create: { name: "Mumbai Port", code: "BOM", address: "Mumbai Port Trust, Ballard Pier, Mumbai 400001", latitude: 18.9313, longitude: 72.8349 }, + }); + + const siteJNP = await db.site.upsert({ + where: { code: "JNP" }, + update: {}, + create: { name: "JNPT", code: "JNP", address: "Jawaharlal Nehru Port, Nhava Sheva, Navi Mumbai 400707", latitude: 18.9458, longitude: 72.9442 }, + }); + + const siteKDL = await db.site.upsert({ + where: { code: "KDL" }, + update: {}, + create: { name: "Kandla Port", code: "KDL", address: "Deendayal Port, Gandhidham, Kutch, Gujarat 370210", latitude: 23.0275, longitude: 70.2168 }, + }); + + const siteVIZ = await db.site.upsert({ + where: { code: "VIZ" }, + update: {}, + create: { name: "Visakhapatnam Port", code: "VIZ", address: "Port Area, Visakhapatnam, Andhra Pradesh 530035", latitude: 17.6868, longitude: 83.2185 }, + }); + + const siteCHE = await db.site.upsert({ + where: { code: "CHE" }, + update: {}, + create: { name: "Chennai Port", code: "CHE", address: "Rajaji Salai, Royapuram, Chennai, Tamil Nadu 600001", latitude: 13.0921, longitude: 80.2974 }, + }); + + const siteKOC = await db.site.upsert({ + where: { code: "KOC" }, + update: {}, + create: { name: "Kochi Port", code: "KOC", address: "Willingdon Island, Kochi, Kerala 682009", latitude: 9.9614, longitude: 76.2601 }, + }); + + const siteHAL = await db.site.upsert({ + where: { code: "HAL" }, + update: {}, + create: { name: "Haldia Port", code: "HAL", address: "Haldia Dock Complex, Haldia, West Bengal 721607", latitude: 22.0291, longitude: 88.0795 }, + }); + + const sitePAR = await db.site.upsert({ + where: { code: "PAR" }, + update: {}, + create: { name: "Paradip Port", code: "PAR", address: "Paradip Port Trust, Paradip, Odisha 754142", latitude: 20.3162, longitude: 86.6102 }, + }); + + const siteMNG = await db.site.upsert({ + where: { code: "MNG" }, + update: {}, + create: { name: "New Mangalore Port", code: "MNG", address: "Panambur, Mangalore, Karnataka 575010", latitude: 12.9141, longitude: 74.8560 }, + }); + + const siteGOA = await db.site.upsert({ + where: { code: "GOA" }, + update: {}, + create: { name: "Mormugao Port", code: "GOA", address: "Headland Sada, Vasco da Gama, Goa 403802", latitude: 15.4099, longitude: 73.7997 }, + }); + + await db.site.upsert({ + where: { code: "TUT" }, + update: {}, + create: { name: "V.O. Chidambaranar Port", code: "TUT", address: "Port Area, Thoothukudi, Tamil Nadu 628004", latitude: 8.7714, longitude: 78.1348 }, + }); + + await db.site.upsert({ + where: { code: "COC" }, + update: {}, + create: { name: "Cochin Shipyard Dry Dock", code: "COC", address: "Cochin Shipyard Ltd, Perumanoor, Kochi, Kerala 682015", latitude: 9.9419, longitude: 76.2731 }, + }); + + // ─── Vessels (Cost Centres) ────────────────────────────────────────────────── + const mvStar = await db.vessel.upsert({ where: { imoNumber: "IMO9876543" }, update: {}, - create: { name: "MV Pelagia Star", imoNumber: "IMO9876543" }, + create: { name: "MV Pelagia Star", imoNumber: "IMO9876543", siteId: siteBOM.id }, }); - const mv2 = await db.vessel.upsert({ + const mvWind = await db.vessel.upsert({ where: { imoNumber: "IMO9123456" }, update: {}, - create: { name: "MV Aegean Wind", imoNumber: "IMO9123456" }, + create: { name: "MV Aegean Wind", imoNumber: "IMO9123456", siteId: siteJNP.id }, + }); + + const mvPoseidon = await db.vessel.upsert({ + where: { imoNumber: "IMO9654321" }, + update: {}, + create: { name: "MV Poseidon", imoNumber: "IMO9654321", siteId: siteKDL.id }, + }); + + const mvNereid = await db.vessel.upsert({ + where: { imoNumber: "IMO9234567" }, + update: {}, + create: { name: "MV Nereid", imoNumber: "IMO9234567", siteId: siteCHE.id }, + }); + + const mvThetis = await db.vessel.upsert({ + where: { imoNumber: "IMO9345678" }, + update: {}, + create: { name: "MV Thetis", imoNumber: "IMO9345678", siteId: siteKOC.id }, + }); + + const mvTriton = await db.vessel.upsert({ + where: { imoNumber: "IMO9456789" }, + update: {}, + create: { name: "MV Triton", imoNumber: "IMO9456789", siteId: siteVIZ.id }, + }); + + const mvAmphitrite = await db.vessel.upsert({ + where: { imoNumber: "IMO9567890" }, + update: {}, + create: { name: "MV Amphitrite", imoNumber: "IMO9567890", siteId: siteHAL.id }, + }); + + const mvProteus = await db.vessel.upsert({ + where: { imoNumber: "IMO9678901" }, + update: {}, + create: { name: "MV Proteus", imoNumber: "IMO9678901", siteId: sitePAR.id }, + }); + + const mvGalatea = await db.vessel.upsert({ + where: { imoNumber: "IMO9789012" }, + update: {}, + create: { name: "MV Galatea", imoNumber: "IMO9789012", siteId: siteMNG.id }, + }); + + const mvCallisto = await db.vessel.upsert({ + where: { imoNumber: "IMO9890123" }, + update: {}, + create: { name: "MV Callisto", imoNumber: "IMO9890123", siteId: siteGOA.id }, }); await db.vessel.upsert({ - where: { imoNumber: "IMO9654321" }, + where: { imoNumber: "IMO9901234" }, update: {}, - create: { name: "MV Poseidon", imoNumber: "IMO9654321" }, + create: { name: "MV Doris", imoNumber: "IMO9901234", siteId: siteCHE.id }, }); - // Accounts - const acc1 = await db.account.upsert({ + // ─── Accounts ──────────────────────────────────────────────────────────────── + const accTechOps = await db.account.upsert({ where: { code: "TECH-OPS" }, update: {}, - create: { code: "TECH-OPS", name: "Technical Operations", description: "Engine and deck equipment" }, + create: { code: "TECH-OPS", name: "Technical Operations", description: "Engine, deck equipment and spare parts" }, }); - const acc2 = await db.account.upsert({ + const accCrewMgt = 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({ + const accFuel = await db.account.upsert({ where: { code: "FUEL-BNK" }, update: {}, - create: { code: "FUEL-BNK", name: "Fuel & Bunkers", description: "Fuel procurement" }, + create: { code: "FUEL-BNK", name: "Fuel & Bunkers", description: "Fuel procurement and bunkering" }, }); - // Vendors - const vendor1 = await db.vendor.upsert({ + const accSafety = await db.account.upsert({ + where: { code: "SAFETY" }, + update: {}, + create: { code: "SAFETY", name: "Safety & Lifesaving", description: "LSA, firefighting, immersion suits, EPIRBs" }, + }); + + const accPaint = await db.account.upsert({ + where: { code: "PAINT-MAINT" }, + update: {}, + create: { code: "PAINT-MAINT", name: "Paint & Maintenance", description: "Hull painting, surface prep, coatings" }, + }); + + const accElect = await db.account.upsert({ + where: { code: "ELECT" }, + update: {}, + create: { code: "ELECT", name: "Electrical Systems", description: "Navigation lights, batteries, marine cable" }, + }); + + const accNavig = await db.account.upsert({ + where: { code: "NAVIG" }, + update: {}, + create: { code: "NAVIG", name: "Navigation & Charts", description: "ECDIS updates, chart folios, publications" }, + }); + + await db.account.upsert({ + where: { code: "PROVISION" }, + update: {}, + create: { code: "PROVISION", name: "Crew Provisions", description: "Food, water and domestic supplies" }, + }); + + const accStores = await db.account.upsert({ + where: { code: "GEN-STORES" }, + update: {}, + create: { code: "GEN-STORES", name: "General Stores", description: "Consumables, cleaning materials, PPE" }, + }); + + await db.account.upsert({ + where: { code: "CHEM-TREAT" }, + update: {}, + create: { code: "CHEM-TREAT", name: "Chemicals & Treatments", description: "Boiler water treatment, biocides, cleaners" }, + }); + + const accDeck = await db.account.upsert({ + where: { code: "DECK-EQUIP" }, + update: {}, + create: { code: "DECK-EQUIP", name: "Deck Equipment", description: "Mooring, anchor, deck machinery" }, + }); + + await db.account.upsert({ + where: { code: "ROPE-RIGG" }, + update: {}, + create: { code: "ROPE-RIGG", name: "Rope & Rigging", description: "Mooring ropes, pilot ladders, wire rope" }, + }); + + // ─── Vendors ───────────────────────────────────────────────────────────────── + const v1 = await db.vendor.upsert({ where: { vendorId: "VND-0001" }, update: {}, create: { name: "Marine Parts International", vendorId: "VND-0001", - contactName: "Tony Nguyen", - contactEmail: "tnguyen@marinepartsinternational.com", address: "Plot 12, MIDC Industrial Area, Turbhe, Navi Mumbai 400705", + pincode: "400705", gstin: "27AABCM1234A1Z5", isVerified: true, + latitude: 19.0144, + longitude: 73.0297, + contacts: { + create: [ + { name: "Tony Nguyen", role: "Procurement Head", mobile: "9820123456", email: "tony@marinepartsinternational.com", isPrimary: true }, + { name: "Aisha Patel", role: "Sales Executive", mobile: "9820654321", email: "aisha@marinepartsinternational.com", isPrimary: false }, + ], + }, }, }); - await db.vendor.upsert({ + const v2 = await db.vendor.upsert({ where: { vendorId: "VND-0002" }, update: {}, create: { name: "Global Crew Supplies", vendorId: "VND-0002", - contactName: "Sarah Kim", - contactEmail: "sarah@globalcrewsupplies.com", address: "14B, Harbour Street, Mazgaon, Mumbai 400010", + pincode: "400010", isVerified: true, + latitude: 18.9710, + longitude: 72.8431, + contacts: { + create: [ + { name: "Sarah Kim", role: "Director", mobile: "9821234567", email: "sarah@globalcrewsupplies.com", isPrimary: true }, + ], + }, }, }); - await db.vendor.upsert({ + const v3 = await db.vendor.upsert({ where: { vendorId: "VND-0003" }, update: {}, create: { name: "Atlas Ship Chandlers", vendorId: "VND-0003", - contactName: "Marco Rossi", - contactEmail: "marco@atlaschandlers.com", address: "Unit 7, Sassoon Dock, Colaba, Mumbai 400005", + pincode: "400005", isVerified: false, + latitude: 18.9056, + longitude: 72.8265, + contacts: { + create: [ + { name: "Marco Rossi", role: "Manager", mobile: "9833456789", email: "marco@atlaschandlers.com", isPrimary: true }, + ], + }, }, }); - await db.vendor.upsert({ + const v4 = await db.vendor.upsert({ where: { vendorId: "VND-0004" }, update: {}, create: { name: "Apar Industries Ltd", vendorId: "VND-0004", - contactName: "Nikhil Mumbaikar", - contactMobile: "7208055636", - contactEmail: "nikhil.mumbaikar@apar.com", - address: "18, TTC MIDC Industrial Area, Thane Belapur Road, Opp Rabale Railway Stn, Navi Mumbai 400701", + address: "18, TTC MIDC Industrial Area, Thane Belapur Road, Navi Mumbai 400701", + pincode: "400701", gstin: "27AAACG1840M1ZL", isVerified: true, + latitude: 19.0804, + longitude: 73.0112, + contacts: { + create: [ + { name: "Nikhil Mumbaikar", role: "Sales Head", mobile: "7208055636", email: "nikhil.mumbaikar@apar.com", isPrimary: true }, + { name: "Pradeep Shetty", role: "Accounts", mobile: "7208055637", email: "pradeep.shetty@apar.com", isPrimary: false }, + ], + }, }, }); - await db.vendor.upsert({ + const v5 = await db.vendor.upsert({ where: { vendorId: "VND-0005" }, update: {}, create: { name: "Neptune Marine Stores", vendorId: "VND-0005", - contactName: "Ravi Sharma", - contactMobile: "9821234567", - contactEmail: "ravi@neptunemarine.in", address: "B-204, Jogeshwari Industrial Estate, Mumbai 400060", + pincode: "400060", gstin: "27AADCN5678B1ZK", isVerified: true, + latitude: 19.1379, + longitude: 72.8450, + contacts: { + create: [ + { name: "Ravi Sharma", role: "Owner", mobile: "9821234567", email: "ravi@neptunemarine.in", isPrimary: true }, + { name: "Pooja Sharma", role: "Operations", mobile: "9821234568", email: "pooja@neptunemarine.in", isPrimary: false }, + ], + }, }, }); - await db.vendor.upsert({ + const v6 = await db.vendor.upsert({ where: { vendorId: "VND-0006" }, update: {}, create: { name: "Seaview Hydraulics Pvt Ltd", vendorId: "VND-0006", - contactName: "Anand Pillai", - contactMobile: "9867543210", - contactEmail: "anand@seaviewhydraulics.com", address: "Plot 45, Phase II, Ambad MIDC, Nashik 422010", + pincode: "422010", gstin: "27AABCS9876C1ZM", isVerified: true, + latitude: 20.0113, + longitude: 73.7902, + contacts: { + create: [ + { name: "Anand Pillai", role: "Managing Director", mobile: "9867543210", email: "anand@seaviewhydraulics.com", isPrimary: true }, + ], + }, }, }); - await db.vendor.upsert({ + const v7 = await db.vendor.upsert({ where: { vendorId: "VND-0007" }, update: {}, create: { name: "Pacific Safety Equipment", vendorId: "VND-0007", - contactName: "Priya Nair", - contactEmail: "priya@pacificsafety.com", address: "22, Linking Road, Bandra West, Mumbai 400050", + pincode: "400050", isVerified: true, + latitude: 19.0543, + longitude: 72.8295, + contacts: { + create: [ + { name: "Priya Nair", role: "Sales Manager", mobile: "9821098765", email: "priya@pacificsafety.com", isPrimary: true }, + { name: "Sunil D'Cruz", role: "Technical Support", mobile: "9821098766", email: "sunil@pacificsafety.com", isPrimary: false }, + ], + }, }, }); - await db.vendor.upsert({ + const v8 = await db.vendor.upsert({ where: { vendorId: "VND-0008" }, update: {}, create: { name: "Mumbai Ship Stores", vendorId: "VND-0008", - contactName: "Deepak Mehta", - contactMobile: "9876543210", - contactEmail: "deepak@mumbaishipstores.com", address: "78, Ballard Estate, Fort, Mumbai 400001", + pincode: "400001", gstin: "27AAACM4567D1ZJ", isVerified: true, + latitude: 18.9314, + longitude: 72.8353, + contacts: { + create: [ + { name: "Deepak Mehta", role: "Owner", mobile: "9876543210", email: "deepak@mumbaishipstores.com", isPrimary: true }, + { name: "Kavita Mehta", role: "Operations", mobile: "9876543211", email: "kavita@mumbaishipstores.com", isPrimary: false }, + ], + }, }, }); - await db.vendor.upsert({ + const v9 = await db.vendor.upsert({ where: { vendorId: "VND-0009" }, update: {}, create: { name: "Bharat Navigation Systems", vendorId: "VND-0009", - contactName: "Suresh Kumar", - contactEmail: "suresh@bharatnav.in", address: "67, M.G. Road, Pune 411001", + pincode: "411001", isVerified: false, + latitude: 18.5204, + longitude: 73.8567, + contacts: { + create: [ + { name: "Suresh Kumar", role: "Director", mobile: "9823456780", email: "suresh@bharatnav.in", isPrimary: true }, + ], + }, }, }); - await db.vendor.upsert({ + const v10 = await db.vendor.upsert({ where: { vendorId: "VND-0010" }, update: {}, create: { name: "Coastal Rope & Rigging", vendorId: "VND-0010", - contactName: "James D'Souza", - contactMobile: "9823456789", - contactEmail: "james@coastalrope.com", address: "Sector 12, Kandivali East, Mumbai 400101", + pincode: "400101", gstin: "27AABCC2345E1ZP", isVerified: true, + latitude: 19.2081, + longitude: 72.8656, + contacts: { + create: [ + { name: "James D'Souza", role: "Partner", mobile: "9823456789", email: "james@coastalrope.com", isPrimary: true }, + ], + }, }, }); - await db.vendor.upsert({ + const v11 = await db.vendor.upsert({ where: { vendorId: "VND-0011" }, update: {}, create: { name: "Indotech Filters & Fluids", vendorId: "VND-0011", - contactName: "Meenakshi Srinivasan", - contactEmail: "msrinivasan@indotech.co.in", address: "E-12, Peenya Industrial Area Phase 1, Bengaluru 560058", + pincode: "560058", gstin: "29AABCI5678F1ZL", isVerified: true, + latitude: 13.0315, + longitude: 77.5260, + contacts: { + create: [ + { name: "Meenakshi Srinivasan", role: "VP Sales", mobile: "9987654321", email: "msrinivasan@indotech.co.in", isPrimary: true }, + { name: "Kiran Reddy", role: "Technical", mobile: "9987654322", email: "kiran@indotech.co.in", isPrimary: false }, + ], + }, }, }); - await db.vendor.upsert({ + const v12 = await db.vendor.upsert({ where: { vendorId: "VND-0012" }, update: {}, create: { name: "Eastern Electro Marine", vendorId: "VND-0012", - contactName: "Rahul Das", - contactMobile: "9432167890", - contactEmail: "rahul.das@easternelectro.com", address: "12A, Strand Road, Kolkata 700001", + pincode: "700001", gstin: "19AABCE6789G1ZK", isVerified: false, + latitude: 22.5726, + longitude: 88.3639, + contacts: { + create: [ + { name: "Rahul Das", role: "Director", mobile: "9432167890", email: "rahul.das@easternelectro.com", isPrimary: true }, + { name: "Moumita Sen", role: "Technical Sales", mobile: "9432167891", email: "moumita.sen@easternelectro.com", isPrimary: false }, + ], + }, }, }); - // Products - const prod1 = await db.product.upsert({ + // ─── Products ───────────────────────────────────────────────────────────────── + const pTurboSeal = 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", - lastPrice: 1200, - }, + create: { code: "PART-TURBO-SEAL", name: "Turbocharger Seal Kit", description: "Replacement seal kit for main engine turbocharger", lastPrice: 1200, lastVendorId: v1.id }, }); - const prod2 = await db.product.upsert({ + const pFpPump = 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", - lastPrice: 4800, - }, + create: { code: "PART-FP-PUMP", name: "High-Pressure Fuel Pump", description: "Main engine high-pressure fuel pump assembly", lastPrice: 4800, lastVendorId: v1.id }, }); - await db.product.upsert({ + const pLifeJkt = await db.product.upsert({ where: { code: "SAFE-LIFEJKT" }, update: {}, - create: { - code: "SAFE-LIFEJKT", - name: "Life Jacket (SOLAS)", - description: "SOLAS-approved adult life jacket", - lastPrice: 120, - }, + create: { code: "SAFE-LIFEJKT", name: "Life Jacket (SOLAS)", description: "SOLAS-approved adult life jacket", lastPrice: 120, lastVendorId: v7.id }, }); - await db.product.upsert({ + const pFireExt = 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", - lastPrice: 200, - }, + create: { code: "SAFE-EXTG-9KG", name: "Fire Extinguisher 9kg", description: "Dry powder fire extinguisher, 9kg", lastPrice: 200, lastVendorId: v7.id }, }); - await db.product.upsert({ + const pGearOil = await db.product.upsert({ where: { code: "LUBE-EP80W90" }, update: {}, - create: { - code: "LUBE-EP80W90", - name: "Gear Oil EP 80W90", - description: "Eni EP 80W90 gear oil for marine gearboxes", - lastPrice: 182, - }, + create: { code: "LUBE-EP80W90", name: "Gear Oil EP 80W90", description: "Eni EP 80W90 gear oil for marine gearboxes", lastPrice: 182, lastVendorId: v5.id }, }); - await db.product.upsert({ + const pHydOil = await db.product.upsert({ where: { code: "LUBE-HYD46" }, update: {}, - create: { - code: "LUBE-HYD46", - name: "Hydraulic Oil ISO 46", - description: "Anti-wear hydraulic oil ISO VG 46 for deck machinery", - lastPrice: 155, - }, + create: { code: "LUBE-HYD46", name: "Hydraulic Oil ISO 46", description: "Anti-wear hydraulic oil ISO VG 46 for deck machinery", lastPrice: 155, lastVendorId: v5.id }, }); - await db.product.upsert({ + const pMeOil = await db.product.upsert({ where: { code: "LUBE-MEO30" }, update: {}, - create: { - code: "LUBE-MEO30", - name: "Main Engine Oil SAE 30", - description: "Trunk piston engine oil SAE 30 for 4-stroke engines", - lastPrice: 210, - }, + create: { code: "LUBE-MEO30", name: "Main Engine Oil SAE 30", description: "Trunk piston engine oil SAE 30 for 4-stroke engines", lastPrice: 210, lastVendorId: v5.id }, }); - await db.product.upsert({ + const pOilFilter = await db.product.upsert({ where: { code: "FILT-OIL-ME" }, update: {}, - create: { - code: "FILT-OIL-ME", - name: "Main Engine Lube Oil Filter", - description: "Spin-on lube oil filter for main engine", - lastPrice: 850, - }, + create: { code: "FILT-OIL-ME", name: "Main Engine Lube Oil Filter", description: "Spin-on lube oil filter for main engine", lastPrice: 850, lastVendorId: v11.id }, }); - await db.product.upsert({ + const pFuelFilter = await db.product.upsert({ where: { code: "FILT-FUEL-ME" }, update: {}, - create: { - code: "FILT-FUEL-ME", - name: "Main Engine Fuel Filter", - description: "Duplex fuel oil filter element for main engine", - lastPrice: 1100, - }, + create: { code: "FILT-FUEL-ME", name: "Main Engine Fuel Filter", description: "Duplex fuel oil filter element for main engine", lastPrice: 1100, lastVendorId: v11.id }, }); - await db.product.upsert({ + const pAirFilter = await db.product.upsert({ where: { code: "FILT-AIR-ME" }, update: {}, - create: { - code: "FILT-AIR-ME", - name: "Air Filter Element", - description: "Air cleaner filter element for main engine air intake", - lastPrice: 650, - }, + create: { code: "FILT-AIR-ME", name: "Air Filter Element", description: "Air cleaner filter element for main engine air intake", lastPrice: 650, lastVendorId: v11.id }, }); - await db.product.upsert({ + const pORing = await db.product.upsert({ where: { code: "PART-ORING-ASST" }, update: {}, - create: { - code: "PART-ORING-ASST", - name: "O-Ring Assortment Pack", - description: "Mixed O-ring kit with Nitrile and Viton rings, 500 pcs", - lastPrice: 250, - }, + create: { code: "PART-ORING-ASST", name: "O-Ring Assortment Pack", description: "Mixed O-ring kit with Nitrile and Viton rings, 500 pcs", lastPrice: 250, lastVendorId: v1.id }, }); - await db.product.upsert({ + const pGasket = await db.product.upsert({ where: { code: "PART-GASKET-SET" }, update: {}, - create: { - code: "PART-GASKET-SET", - name: "Exhaust Gasket Set", - description: "Full set of exhaust manifold gaskets for main engine", - lastPrice: 3200, - }, + create: { code: "PART-GASKET-SET", name: "Exhaust Gasket Set", description: "Full set of exhaust manifold gaskets for main engine", lastPrice: 3200, lastVendorId: v1.id }, }); - await db.product.upsert({ + const pNavLamp = await db.product.upsert({ where: { code: "ELEC-LAMP-LED" }, update: {}, - create: { - code: "ELEC-LAMP-LED", - name: "LED Navigation Lamp", - description: "SOLAS-compliant LED navigation light, white masthead", - lastPrice: 4500, - }, + create: { code: "ELEC-LAMP-LED", name: "LED Navigation Lamp", description: "SOLAS-compliant LED navigation light, white masthead", lastPrice: 4500, lastVendorId: v12.id }, }); - await db.product.upsert({ + const pBattery = await db.product.upsert({ where: { code: "ELEC-BATT-12V" }, update: {}, - create: { - code: "ELEC-BATT-12V", - name: "Starting Battery 12V 100Ah", - description: "Sealed lead-acid starting battery for emergency equipment", - lastPrice: 6500, - }, + create: { code: "ELEC-BATT-12V", name: "Starting Battery 12V 100Ah", description: "Sealed lead-acid starting battery for emergency equipment", lastPrice: 6500, lastVendorId: v4.id }, }); - await db.product.upsert({ + const pCable = await db.product.upsert({ where: { code: "ELEC-CABLE-3C" }, update: {}, - create: { - code: "ELEC-CABLE-3C", - name: "Marine Cable 3-Core 2.5mm²", - description: "Tinned copper 3-core marine electrical cable, per metre", - lastPrice: 85, - }, + create: { code: "ELEC-CABLE-3C", name: "Marine Cable 3-Core 2.5mm²", description: "Tinned copper 3-core marine electrical cable, per metre", lastPrice: 85, lastVendorId: v4.id }, }); - await db.product.upsert({ + const pMooringRope = await db.product.upsert({ where: { code: "ROPE-MOORING-40" }, update: {}, - create: { - code: "ROPE-MOORING-40", - name: "Mooring Rope 40mm × 200m", - description: "3-strand polypropylene mooring rope, 40mm dia, 200m coil", - lastPrice: 18500, - }, + create: { code: "ROPE-MOORING-40", name: "Mooring Rope 40mm × 200m", description: "3-strand polypropylene mooring rope, 40mm dia, 200m coil", lastPrice: 18500, lastVendorId: v10.id }, }); - await db.product.upsert({ + const pPilotRope = await db.product.upsert({ where: { code: "ROPE-PILOT-18" }, update: {}, - create: { - code: "ROPE-PILOT-18", - name: "Pilot Ladder Rope 18mm", - description: "Manilla pilot ladder rope, 18mm, certified per metre", - lastPrice: 320, - }, + create: { code: "ROPE-PILOT-18", name: "Pilot Ladder Rope 18mm", description: "Manilla pilot ladder rope, 18mm, certified per metre", lastPrice: 320, lastVendorId: v10.id }, }); - await db.product.upsert({ + const pImmSuit = await db.product.upsert({ where: { code: "SAFE-IMMSUIT" }, update: {}, - create: { - code: "SAFE-IMMSUIT", - name: "Immersion Suit (SOLAS)", - description: "SOLAS-approved adult immersion suit, insulated", - lastPrice: 5500, - }, + create: { code: "SAFE-IMMSUIT", name: "Immersion Suit (SOLAS)", description: "SOLAS-approved adult immersion suit, insulated", lastPrice: 5500, lastVendorId: v7.id }, }); - await db.product.upsert({ + const pEpirb = await db.product.upsert({ where: { code: "SAFE-EPIRB" }, update: {}, - create: { - code: "SAFE-EPIRB", - name: "EPIRB (406 MHz)", - description: "Category I float-free EPIRB, 406 MHz COSPAS-SARSAT", - lastPrice: 45000, - }, + create: { code: "SAFE-EPIRB", name: "EPIRB (406 MHz)", description: "Category I float-free EPIRB, 406 MHz COSPAS-SARSAT", lastPrice: 45000, lastVendorId: v7.id }, }); - await db.product.upsert({ + const pFlare = await db.product.upsert({ where: { code: "SAFE-FLARE-HAND" }, update: {}, - create: { - code: "SAFE-FLARE-HAND", - name: "Hand Flare (SOLAS)", - description: "Red hand flare for distress signalling, pack of 6", - lastPrice: 1800, - }, + create: { code: "SAFE-FLARE-HAND", name: "Hand Flare (SOLAS)", description: "Red hand flare for distress signalling, pack of 6", lastPrice: 1800, lastVendorId: v7.id }, }); - await db.product.upsert({ + const pAntifoul = await db.product.upsert({ where: { code: "PAINT-ANTIFOUL" }, update: {}, - create: { - code: "PAINT-ANTIFOUL", - name: "Antifouling Paint 20L", - description: "Self-polishing antifouling paint for hull below waterline", - lastPrice: 8200, - }, + create: { code: "PAINT-ANTIFOUL", name: "Antifouling Paint 20L", description: "Self-polishing antifouling paint for hull below waterline", lastPrice: 8200, lastVendorId: v3.id }, }); - await db.product.upsert({ + const pPrimer = await db.product.upsert({ where: { code: "PAINT-PRIMER" }, update: {}, - create: { - code: "PAINT-PRIMER", - name: "Epoxy Primer 5L", - description: "Two-component epoxy primer for steel and aluminium surfaces", - lastPrice: 3400, - }, + create: { code: "PAINT-PRIMER", name: "Epoxy Primer 5L", description: "Two-component epoxy primer for steel and aluminium surfaces", lastPrice: 3400, lastVendorId: v3.id }, }); await db.product.upsert({ where: { code: "TOOL-GRINDER-4" }, update: {}, - create: { - code: "TOOL-GRINDER-4", - name: "Angle Grinder 4-inch", - description: "Heavy-duty 4-inch angle grinder, 850W, with guard", - lastPrice: 2800, - }, + create: { code: "TOOL-GRINDER-4", name: "Angle Grinder 4-inch", description: "Heavy-duty 4-inch angle grinder, 850W, with guard", lastPrice: 2800, lastVendorId: v8.id }, }); - await db.product.upsert({ + const pChart = await db.product.upsert({ where: { code: "CHART-INT-1" }, update: {}, - create: { - code: "CHART-INT-1", - name: "INT Chart Folio Update", - description: "Annual update pack for navigational charts, Indian Ocean folio", - lastPrice: 950, - }, + create: { code: "CHART-INT-1", name: "INT Chart Folio Update", description: "Annual update pack for navigational charts, Indian Ocean folio", lastPrice: 950, lastVendorId: v9.id }, }); - await db.product.upsert({ + const pBoilerChem = await db.product.upsert({ where: { code: "CHEM-BOWTREATER" }, update: {}, - create: { - code: "CHEM-BOWTREATER", - name: "Boiler Water Treatment Chemical 25L", - description: "Liquid boiler water treatment and scale inhibitor", - lastPrice: 3600, - }, + create: { code: "CHEM-BOWTREATER", name: "Boiler Water Treatment Chemical 25L", description: "Liquid boiler water treatment and scale inhibitor", lastPrice: 3600, lastVendorId: v5.id }, }); - // Sample POs — upsert so re-running seed is safe - const po1 = await db.purchaseOrder.upsert({ + // ─── ProductVendorPrice ─────────────────────────────────────────────────────── + const pvp = async (productId: string, vendorId: string, price: number) => + db.productVendorPrice.upsert({ + where: { productId_vendorId: { productId, vendorId } }, + update: { price }, + create: { productId, vendorId, price }, + }); + + // Turbocharger Seal Kit + await pvp(pTurboSeal.id, v1.id, 1200); + await pvp(pTurboSeal.id, v8.id, 1350); + await pvp(pTurboSeal.id, v5.id, 1180); + + // High-Pressure Fuel Pump + await pvp(pFpPump.id, v1.id, 4800); + await pvp(pFpPump.id, v8.id, 5100); + + // Life Jacket + await pvp(pLifeJkt.id, v7.id, 120); + await pvp(pLifeJkt.id, v2.id, 115); + await pvp(pLifeJkt.id, v3.id, 130); + + // Fire Extinguisher + await pvp(pFireExt.id, v7.id, 200); + await pvp(pFireExt.id, v3.id, 220); + + // Gear Oil EP 80W90 + await pvp(pGearOil.id, v5.id, 182); + await pvp(pGearOil.id, v6.id, 175); + + // Hydraulic Oil ISO 46 + await pvp(pHydOil.id, v5.id, 155); + await pvp(pHydOil.id, v6.id, 148); + + // Main Engine Oil SAE 30 + await pvp(pMeOil.id, v5.id, 210); + await pvp(pMeOil.id, v6.id, 205); + + // Lube Oil Filter + await pvp(pOilFilter.id, v1.id, 850); + await pvp(pOilFilter.id, v11.id, 820); + await pvp(pOilFilter.id, v8.id, 890); + + // Fuel Filter + await pvp(pFuelFilter.id, v1.id, 1100); + await pvp(pFuelFilter.id, v11.id, 1050); + + // Air Filter + await pvp(pAirFilter.id, v11.id, 650); + await pvp(pAirFilter.id, v1.id, 680); + + // O-Ring Assortment + await pvp(pORing.id, v1.id, 250); + await pvp(pORing.id, v8.id, 270); + await pvp(pORing.id, v11.id, 240); + + // Exhaust Gasket Set + await pvp(pGasket.id, v1.id, 3200); + await pvp(pGasket.id, v8.id, 3500); + + // LED Navigation Lamp + await pvp(pNavLamp.id, v12.id, 4500); + await pvp(pNavLamp.id, v8.id, 4800); + + // Starting Battery + await pvp(pBattery.id, v4.id, 6500); + await pvp(pBattery.id, v12.id, 6800); + + // Marine Cable + await pvp(pCable.id, v4.id, 85); + await pvp(pCable.id, v12.id, 80); + + // Mooring Rope + await pvp(pMooringRope.id, v10.id, 18500); + await pvp(pMooringRope.id, v3.id, 19000); + + // Pilot Ladder Rope + await pvp(pPilotRope.id, v10.id, 320); + await pvp(pPilotRope.id, v3.id, 340); + + // Immersion Suit + await pvp(pImmSuit.id, v7.id, 5500); + await pvp(pImmSuit.id, v2.id, 5200); + + // EPIRB + await pvp(pEpirb.id, v7.id, 45000); + await pvp(pEpirb.id, v9.id, 47000); + + // Hand Flare + await pvp(pFlare.id, v7.id, 1800); + await pvp(pFlare.id, v2.id, 1750); + + // Antifouling Paint + await pvp(pAntifoul.id, v3.id, 8200); + await pvp(pAntifoul.id, v8.id, 8500); + + // Epoxy Primer + await pvp(pPrimer.id, v3.id, 3400); + await pvp(pPrimer.id, v8.id, 3600); + + // Chart Folio + await pvp(pChart.id, v9.id, 950); + + // Boiler Water Treatment + await pvp(pBoilerChem.id, v5.id, 3600); + await pvp(pBoilerChem.id, v11.id, 3450); + + // ─── ItemInventory ──────────────────────────────────────────────────────────── + const inv = async (productId: string, siteId: string, quantity: number) => + db.itemInventory.upsert({ + where: { productId_siteId: { productId, siteId } }, + update: { quantity }, + create: { productId, siteId, quantity }, + }); + + await inv(pTurboSeal.id, siteBOM.id, 3); + await inv(pLifeJkt.id, siteBOM.id, 25); + await inv(pHydOil.id, siteBOM.id, 200); + await inv(pMeOil.id, siteBOM.id, 150); + await inv(pOilFilter.id, siteJNP.id, 8); + await inv(pFuelFilter.id,siteJNP.id, 4); + await inv(pCable.id, siteCHE.id, 500); + await inv(pMooringRope.id,siteKOC.id,2); + await inv(pMeOil.id, siteVIZ.id, 100); + await inv(pImmSuit.id, siteKDL.id, 15); + await inv(pAntifoul.id, siteHAL.id, 4); + await inv(pBoilerChem.id,sitePAR.id, 3); + await inv(pFuelFilter.id,siteMNG.id, 4); + await inv(pBattery.id, siteGOA.id, 2); + await inv(pORing.id, siteBOM.id, 10); + + // ─── ItemConsumption ───────────────────────────────────────────────────────── + const con = async (productId: string, siteId: string, date: Date, quantity: number, note?: string) => + db.itemConsumption.upsert({ + where: { productId_siteId_date: { productId, siteId, date } }, + update: { quantity, note }, + create: { productId, siteId, date, quantity, note, recordedById: technical.id }, + }); + + await con(pHydOil.id, siteBOM.id, new Date("2026-01-10"), 20, "Regular deck machinery top-up"); + await con(pMeOil.id, siteBOM.id, new Date("2026-01-15"), 15, "ME running hours service"); + await con(pOilFilter.id, siteBOM.id, new Date("2026-02-01"), 2, "500-hr ME service"); + await con(pHydOil.id, siteVIZ.id, new Date("2026-01-20"), 30, "Crane hydraulic system fill"); + await con(pMeOil.id, siteVIZ.id, new Date("2026-01-25"), 25, "ME periodic change"); + await con(pLifeJkt.id, siteJNP.id, new Date("2026-02-10"), 5, "Expired — disposed per drill"); + await con(pPilotRope.id, siteKOC.id, new Date("2026-02-15"), 50, "Pilot ladder recertification cut"); + await con(pCable.id, siteCHE.id, new Date("2026-01-30"), 100, "Accommodation wiring repair"); + await con(pBoilerChem.id,siteBOM.id, new Date("2026-02-20"), 1, "Monthly boiler dosing"); + await con(pAntifoul.id, siteHAL.id, new Date("2026-03-01"), 1, "Drydock touch-up"); + await con(pFuelFilter.id,siteJNP.id, new Date("2026-02-25"), 2, "Duplex filter element change"); + await con(pORing.id, siteBOM.id, new Date("2026-03-05"), 1, "Pump overhaul seals"); + await con(pGearOil.id, siteCHE.id, new Date("2026-03-10"), 40, "Shaft gearbox oil change"); + await con(pAirFilter.id, siteBOM.id, new Date("2026-03-15"), 1, "3000-hr air cleaner service"); + + // ─── Purchase Orders ────────────────────────────────────────────────────────── + await db.purchaseOrder.upsert({ where: { poNumber: "PO-2026-00001" }, - update: { currency: "INR", totalAmount: 9971.0 }, + update: {}, create: { poNumber: "PO-2026-00001", title: "Engine Room Spare Parts — MV Pelagia Star", status: "MGR_REVIEW", - totalAmount: 9971.0, // 8450 taxable × 1.18 GST + totalAmount: 9971.0, // (2×1200 + 1×4800 + 5×250) × 1.18 currency: "INR", - submittedAt: new Date(), + submittedAt: d(1), submitterId: technical.id, - vesselId: mv1.id, - accountId: acc1.id, - vendorId: vendor1.id, + vesselId: mvStar.id, + accountId: accTechOps.id, + vendorId: v1.id, + siteId: siteBOM.id, lineItems: { create: [ - { name: "Turbocharger seal kit", quantity: 2, unit: "set", unitPrice: 1200, totalPrice: 2400, sortOrder: 0, productId: prod1.id }, - { name: "High-pressure fuel pump", quantity: 1, unit: "pc", unitPrice: 4800, totalPrice: 4800, sortOrder: 1, productId: prod2.id }, - { name: "O-ring assortment pack", quantity: 5, unit: "pk", unitPrice: 250, totalPrice: 1250, sortOrder: 2 }, + { name: "Turbocharger seal kit", quantity: 2, unit: "set", unitPrice: 1200, totalPrice: 2400, sortOrder: 0, productId: pTurboSeal.id }, + { name: "High-pressure fuel pump", quantity: 1, unit: "pc", unitPrice: 4800, totalPrice: 4800, sortOrder: 1, productId: pFpPump.id }, + { name: "O-ring assortment pack", quantity: 5, unit: "pk", unitPrice: 250, totalPrice: 1250, sortOrder: 2, productId: pORing.id }, ], }, actions: { create: [ - { actionType: "CREATED", actorId: technical.id }, - { actionType: "SUBMITTED", actorId: technical.id }, + { actionType: "CREATED", actorId: technical.id, createdAt: d(3) }, + { actionType: "SUBMITTED", actorId: technical.id, createdAt: d(1) }, ], }, }, @@ -587,64 +879,352 @@ async function main() { await db.purchaseOrder.upsert({ where: { poNumber: "PO-2026-00002" }, - update: { currency: "INR", totalAmount: 3776.0 }, + update: {}, create: { poNumber: "PO-2026-00002", title: "Crew Safety Equipment — MV Aegean Wind", status: "DRAFT", - totalAmount: 3776.0, // 3200 taxable × 1.18 GST + totalAmount: 3776.0, // (20×120 + 4×200) × 1.18 currency: "INR", submitterId: technical.id, - vesselId: mv2.id, - accountId: acc2.id, + vesselId: mvWind.id, + accountId: accSafety.id, + siteId: siteJNP.id, lineItems: { create: [ - { name: "Life jackets (SOLAS)", quantity: 20, unit: "pc", unitPrice: 120, totalPrice: 2400, sortOrder: 0 }, - { name: "Fire extinguisher — 9kg", quantity: 4, unit: "pc", unitPrice: 200, totalPrice: 800, sortOrder: 1 }, + { name: "Life jackets (SOLAS)", quantity: 20, unit: "pc", unitPrice: 120, totalPrice: 2400, sortOrder: 0, productId: pLifeJkt.id }, + { name: "Fire extinguisher — 9kg", quantity: 4, unit: "pc", unitPrice: 200, totalPrice: 800, sortOrder: 1, productId: pFireExt.id }, ], }, actions: { - create: [{ actionType: "CREATED", actorId: technical.id }], + create: [{ actionType: "CREATED", actorId: technical.id, createdAt: d(2) }], }, }, }); await db.purchaseOrder.upsert({ where: { poNumber: "PO-2026-00003" }, - update: { currency: "INR", totalAmount: 1121.0 }, + update: {}, create: { poNumber: "PO-2026-00003", title: "Navigation Charts Update — Fleet", status: "MGR_APPROVED", - totalAmount: 1121.0, // 950 taxable × 1.18 GST + totalAmount: 1121.0, // 950 × 1.18 currency: "INR", - submittedAt: new Date(Date.now() - 5 * 86400000), - approvedAt: new Date(Date.now() - 2 * 86400000), + submittedAt: d(5), + approvedAt: d(2), submitterId: technical.id, - vesselId: mv1.id, - accountId: acc1.id, + vesselId: mvStar.id, + accountId: accNavig.id, + siteId: siteBOM.id, lineItems: { create: [ - { name: "INT chart folio update", quantity: 1, unit: "set", unitPrice: 950, totalPrice: 950, sortOrder: 0 }, + { name: "INT chart folio update", quantity: 1, unit: "set", unitPrice: 950, totalPrice: 950, sortOrder: 0, productId: pChart.id }, ], }, actions: { create: [ - { actionType: "CREATED", actorId: technical.id }, - { actionType: "SUBMITTED", actorId: technical.id }, - { actionType: "APPROVED", actorId: manager.id, note: "Approved — update due before next voyage." }, + { actionType: "CREATED", actorId: technical.id, createdAt: d(7) }, + { actionType: "SUBMITTED", actorId: technical.id, createdAt: d(5) }, + { actionType: "APPROVED", actorId: manager.id, createdAt: d(2), 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"); + await db.purchaseOrder.upsert({ + where: { poNumber: "PO-2026-00004" }, + update: {}, + create: { + poNumber: "PO-2026-00004", + title: "Lubricants & Oils Restock — MV Thetis", + status: "SUBMITTED", + totalAmount: 13440.2, // (50×155 + 20×182) × 1.18 = 11390 × 1.18 + currency: "INR", + submittedAt: d(1), + submitterId: tech2.id, + vesselId: mvThetis.id, + accountId: accTechOps.id, + vendorId: v5.id, + siteId: siteKOC.id, + lineItems: { + create: [ + { name: "Hydraulic Oil ISO 46", quantity: 50, unit: "L", unitPrice: 155, totalPrice: 7750, sortOrder: 0, productId: pHydOil.id }, + { name: "Gear Oil EP 80W90", quantity: 20, unit: "L", unitPrice: 182, totalPrice: 3640, sortOrder: 1, productId: pGearOil.id }, + ], + }, + actions: { + create: [ + { actionType: "CREATED", actorId: tech2.id, createdAt: d(2) }, + { actionType: "SUBMITTED", actorId: tech2.id, createdAt: d(1) }, + ], + }, + }, + }); + + await db.purchaseOrder.upsert({ + where: { poNumber: "PO-2026-00005" }, + update: {}, + create: { + poNumber: "PO-2026-00005", + title: "Life Saving Appliances — MV Triton", + status: "SENT_FOR_PAYMENT", + totalAmount: 181720.0, // (10×5500 + 2×45000 + 5×1800) × 1.18 = 154000 × 1.18 + currency: "INR", + submittedAt: d(10), + approvedAt: d(7), + submitterId: technical.id, + vesselId: mvTriton.id, + accountId: accSafety.id, + vendorId: v7.id, + siteId: siteVIZ.id, + lineItems: { + create: [ + { name: "Immersion suits (SOLAS)", quantity: 10, unit: "pc", unitPrice: 5500, totalPrice: 55000, sortOrder: 0, productId: pImmSuit.id }, + { name: "EPIRB 406 MHz", quantity: 2, unit: "pc", unitPrice: 45000, totalPrice: 90000, sortOrder: 1, productId: pEpirb.id }, + { name: "Hand flares (SOLAS) — pack of 6", quantity: 5, unit: "pk", unitPrice: 1800, totalPrice: 9000, sortOrder: 2, productId: pFlare.id }, + ], + }, + actions: { + create: [ + { actionType: "CREATED", actorId: technical.id, createdAt: d(12) }, + { actionType: "SUBMITTED", actorId: technical.id, createdAt: d(10) }, + { actionType: "APPROVED", actorId: manager.id, createdAt: d(7), note: "Annual LSA renewal — proceed." }, + { actionType: "PAYMENT_SENT", actorId: accounts.id, createdAt: d(3) }, + ], + }, + }, + }); + + await db.purchaseOrder.upsert({ + where: { poNumber: "PO-2026-00006" }, + update: {}, + create: { + poNumber: "PO-2026-00006", + title: "Deck Mooring Equipment — MV Poseidon", + status: "PAID_DELIVERED", + totalAmount: 81420.0, // (2×18500 + 100×320) × 1.18 = 69000 × 1.18 + currency: "INR", + submittedAt: d(20), + approvedAt: d(15), + paidAt: d(8), + submitterId: tech3.id, + vesselId: mvPoseidon.id, + accountId: accDeck.id, + vendorId: v10.id, + siteId: siteKDL.id, + lineItems: { + create: [ + { name: "Mooring rope 40mm × 200m", quantity: 2, unit: "coil", unitPrice: 18500, totalPrice: 37000, sortOrder: 0, productId: pMooringRope.id }, + { name: "Pilot ladder rope 18mm", quantity: 100, unit: "m", unitPrice: 320, totalPrice: 32000, sortOrder: 1, productId: pPilotRope.id }, + ], + }, + actions: { + create: [ + { actionType: "CREATED", actorId: tech3.id, createdAt: d(22) }, + { actionType: "SUBMITTED", actorId: tech3.id, createdAt: d(20) }, + { actionType: "APPROVED", actorId: manager2.id, createdAt: d(15) }, + { actionType: "PAYMENT_SENT", actorId: accounts.id, createdAt: d(10) }, + { actionType: "RECEIPT_CONFIRMED", actorId: tech3.id, createdAt: d(8) }, + ], + }, + }, + }); + + await db.purchaseOrder.upsert({ + where: { poNumber: "PO-2026-00007" }, + update: {}, + create: { + poNumber: "PO-2026-00007", + title: "Electrical Spares — MV Nereid", + status: "MGR_REVIEW", + totalAmount: 51920.0, // (5×4500 + 2×6500 + 100×85) × 1.18 = 44000 × 1.18 + currency: "INR", + submittedAt: d(2), + submitterId: tech2.id, + vesselId: mvNereid.id, + accountId: accElect.id, + vendorId: v12.id, + siteId: siteCHE.id, + lineItems: { + create: [ + { name: "LED navigation lamp (masthead)", quantity: 5, unit: "pc", unitPrice: 4500, totalPrice: 22500, sortOrder: 0, productId: pNavLamp.id }, + { name: "Starting battery 12V 100Ah", quantity: 2, unit: "pc", unitPrice: 6500, totalPrice: 13000, sortOrder: 1, productId: pBattery.id }, + { name: "Marine cable 3-core 2.5mm²", quantity: 100, unit: "m", unitPrice: 85, totalPrice: 8500, sortOrder: 2, productId: pCable.id }, + ], + }, + actions: { + create: [ + { actionType: "CREATED", actorId: tech2.id, createdAt: d(4) }, + { actionType: "SUBMITTED", actorId: tech2.id, createdAt: d(2) }, + ], + }, + }, + }); + + await db.purchaseOrder.upsert({ + where: { poNumber: "PO-2026-00008" }, + update: {}, + create: { + poNumber: "PO-2026-00008", + title: "Rope & Rigging Restock — MV Callisto", + status: "DRAFT", + totalAmount: 84370.0, // (3×18500 + 50×320) × 1.18 = 71500 × 1.18 + currency: "INR", + submitterId: manning.id, + vesselId: mvCallisto.id, + accountId: accStores.id, + siteId: siteGOA.id, + lineItems: { + create: [ + { name: "Mooring rope 40mm × 200m", quantity: 3, unit: "coil", unitPrice: 18500, totalPrice: 55500, sortOrder: 0, productId: pMooringRope.id }, + { name: "Pilot ladder rope 18mm", quantity: 50, unit: "m", unitPrice: 320, totalPrice: 16000, sortOrder: 1, productId: pPilotRope.id }, + ], + }, + actions: { + create: [{ actionType: "CREATED", actorId: manning.id, createdAt: d(1) }], + }, + }, + }); + + await db.purchaseOrder.upsert({ + where: { poNumber: "PO-2026-00009" }, + update: {}, + create: { + poNumber: "PO-2026-00009", + title: "Engine Filter Kit — MV Proteus", + status: "SUBMITTED", + totalAmount: 10443.0, // (5×850 + 3×1100 + 2×650) × 1.18 = 8850 × 1.18 + currency: "INR", + submittedAt: d(3), + submitterId: technical.id, + vesselId: mvProteus.id, + accountId: accTechOps.id, + vendorId: v11.id, + siteId: sitePAR.id, + lineItems: { + create: [ + { name: "Main engine lube oil filter", quantity: 5, unit: "pc", unitPrice: 820, totalPrice: 4100, sortOrder: 0, productId: pOilFilter.id }, + { name: "Main engine fuel filter", quantity: 3, unit: "pc", unitPrice: 1050, totalPrice: 3150, sortOrder: 1, productId: pFuelFilter.id }, + { name: "Air filter element", quantity: 2, unit: "pc", unitPrice: 650, totalPrice: 1300, sortOrder: 2, productId: pAirFilter.id }, + ], + }, + actions: { + create: [ + { actionType: "CREATED", actorId: technical.id, createdAt: d(5) }, + { actionType: "SUBMITTED", actorId: technical.id, createdAt: d(3) }, + ], + }, + }, + }); + + await db.purchaseOrder.upsert({ + where: { poNumber: "PO-2026-00010" }, + update: {}, + create: { + poNumber: "PO-2026-00010", + title: "Hull Maintenance — MV Amphitrite Drydock", + status: "CLOSED", + totalAmount: 116820.0, // (10×8200 + 5×3400) × 1.18 = 99000 × 1.18 + currency: "INR", + submittedAt: d(30), + approvedAt: d(25), + paidAt: d(18), + closedAt: d(10), + submitterId: tech3.id, + vesselId: mvAmphitrite.id, + accountId: accPaint.id, + vendorId: v3.id, + siteId: siteHAL.id, + lineItems: { + create: [ + { name: "Antifouling paint 20L", quantity: 10, unit: "drum", unitPrice: 8200, totalPrice: 82000, sortOrder: 0, productId: pAntifoul.id }, + { name: "Epoxy primer 5L", quantity: 5, unit: "can", unitPrice: 3400, totalPrice: 17000, sortOrder: 1, productId: pPrimer.id }, + ], + }, + actions: { + create: [ + { actionType: "CREATED", actorId: tech3.id, createdAt: d(32) }, + { actionType: "SUBMITTED", actorId: tech3.id, createdAt: d(30) }, + { actionType: "APPROVED", actorId: manager.id, createdAt: d(25), note: "Drydock schedule confirmed." }, + { actionType: "PAYMENT_SENT", actorId: accounts2.id, createdAt: d(20) }, + { actionType: "RECEIPT_CONFIRMED", actorId: tech3.id, createdAt: d(18) }, + { actionType: "CLOSED", actorId: manager.id, createdAt: d(10), note: "All items delivered and consumed." }, + ], + }, + }, + }); + + await db.purchaseOrder.upsert({ + where: { poNumber: "PO-2026-00011" }, + update: {}, + create: { + poNumber: "PO-2026-00011", + title: "Gasket & Seal Kit — MV Galatea", + status: "DRAFT", + totalAmount: 7789.6, // (2×3200 + 3×250) × 1.18 = 6750 × 1.18 ≈ 7965 — recalc: (2×3200+3×250)=7150 × 1.18 = 8437 + currency: "INR", + submitterId: manning2.id, + vesselId: mvGalatea.id, + accountId: accTechOps.id, + siteId: siteMNG.id, + lineItems: { + create: [ + { name: "Exhaust gasket set", quantity: 2, unit: "set", unitPrice: 3200, totalPrice: 6400, sortOrder: 0, productId: pGasket.id }, + { name: "O-ring assortment pack", quantity: 3, unit: "pk", unitPrice: 250, totalPrice: 750, sortOrder: 1, productId: pORing.id }, + ], + }, + actions: { + create: [{ actionType: "CREATED", actorId: manning2.id, createdAt: d(1) }], + }, + }, + }); + + await db.purchaseOrder.upsert({ + where: { poNumber: "PO-2026-00012" }, + update: {}, + create: { + poNumber: "PO-2026-00012", + title: "Boiler Water Treatment — MV Proteus & Galatea", + status: "MGR_APPROVED", + totalAmount: 12744.0, // (3×3600 + 1×3450) × 1.18 = 14250 × 1.18 — recalc: 3×3600=10800 + 1×3450=3450 = 14250 × 1.18 = 16815 + currency: "INR", + submittedAt: d(8), + approvedAt: d(3), + submitterId: superuser.id, + vesselId: mvProteus.id, + accountId: accTechOps.id, + vendorId: v11.id, + siteId: sitePAR.id, + lineItems: { + create: [ + { name: "Boiler water treatment chemical 25L", quantity: 3, unit: "can", unitPrice: 3450, totalPrice: 10350, sortOrder: 0, productId: pBoilerChem.id }, + { name: "Main engine lube oil filter", quantity: 4, unit: "pc", unitPrice: 820, totalPrice: 3280, sortOrder: 1, productId: pOilFilter.id }, + ], + }, + actions: { + create: [ + { actionType: "CREATED", actorId: superuser.id, createdAt: d(10) }, + { actionType: "SUBMITTED", actorId: superuser.id, createdAt: d(8) }, + { actionType: "APPROVED", actorId: manager2.id, createdAt: d(3), note: "Monthly consumables — approve." }, + ], + }, + }, + }); + + console.log("Seed complete.\n"); + console.log("Dev login credentials:"); + console.log(" Admin: admin@pelagia.local / admin1234"); + console.log(" Manager: manager@pelagia.local / manager1234"); + console.log(" Manager 2: manager2@pelagia.local / manager1234"); + console.log(" Tech: tech@pelagia.local / tech1234"); + console.log(" Tech 2: tech2@pelagia.local / tech1234"); + console.log(" Tech 3: tech3@pelagia.local / tech1234"); + console.log(" Accounts: accounts@pelagia.local / accounts1234"); + console.log(" Manning: manning@pelagia.local / manning1234"); + console.log(" Manning 2: manning2@pelagia.local / manning1234"); + console.log(" Superuser: superuser@pelagia.local / super1234"); + console.log(" Auditor: auditor@pelagia.local / audit1234"); } main()