From 0e3a79ecd4e733b521c45a1554f463e61d47ccda Mon Sep 17 00:00:00 2001 From: Hardik Date: Sat, 30 May 2026 04:26:38 +0530 Subject: [PATCH] feat: production seed script for Pelagia Marine (pnpm db:seed:prod) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Idempotent seed with real company data: - 14 users (SSO/no-password): Accounts, Managers, Technical, Manning - 7 sites: Head Office (HOFC), PMS Kochi, Laccadives, Haldia, Thilakkam, Kavaratti, Thinnakara - 10 vessels assigned to their respective sites: HNR1-4, Champion, Hanunam, Sejal, Sejal 2, GD 3000, Thilakkam - Full accounting code hierarchy (Rev. 01/251227) — 300+ codes across 7 top categories via two-pass upsert to wire parent links Run with: pnpm db:seed:prod Co-Authored-By: Claude Sonnet 4.6 --- App/package.json | 1 + App/prisma/seed-prod.ts | 157 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 App/prisma/seed-prod.ts diff --git a/App/package.json b/App/package.json index aae3b77..f481137 100644 --- a/App/package.json +++ b/App/package.json @@ -20,6 +20,7 @@ "db:migrate": "prisma migrate dev", "db:migrate:deploy": "prisma migrate deploy", "db:seed": "tsx prisma/seed.ts", + "db:seed:prod": "tsx prisma/seed-prod.ts", "db:studio": "prisma studio", "db:reset": "prisma migrate reset", "email:preview": "email dev --dir emails --port 3001" diff --git a/App/prisma/seed-prod.ts b/App/prisma/seed-prod.ts new file mode 100644 index 0000000..72751c0 --- /dev/null +++ b/App/prisma/seed-prod.ts @@ -0,0 +1,157 @@ +/** + * Production seed — Pelagia Marine Services + * + * Idempotent: safe to run multiple times (all operations are upsert). + * Run with: pnpm db:seed:prod + * + * Seeds: + * - Users (SSO, no password) + * - Sites + * - Vessels (assigned to sites) + * - Accounting codes (full hierarchy from Rev. 01/251227) + */ + +import { PrismaClient, Role } from "@prisma/client"; +import { ACCOUNTING_CODES } from "./accounting-codes-data"; + +const db = new PrismaClient(); + +// ─── Users ──────────────────────────────────────────────────────────────────── + +const USERS: { employeeId: string; name: string; email: string; role: Role }[] = [ + { employeeId: "PMS-001", name: "Akshata Teli", email: "akshata@pelagiamarine.com", role: Role.ACCOUNTS }, + { employeeId: "PMS-002", name: "Chhagan Sarang", email: "chhagan.sarang@pelagiamarine.com", role: Role.MANAGER }, + { employeeId: "PMS-003", name: "Dipali K", email: "dipali.k@pelagiamarine.com", role: Role.ACCOUNTS }, + { employeeId: "PMS-004", name: "Eeshan Singh", email: "eeshan.singh@pelagiamarine.com", role: Role.TECHNICAL }, + { employeeId: "PMS-005", name: "Kaushal Pal Singh", email: "kps@pelagiamarine.com", role: Role.MANAGER }, + { employeeId: "PMS-006", name: "Manjuprasad B", email: "manjuprasad.b@pelagiamarine.com", role: Role.TECHNICAL }, + { employeeId: "PMS-007", name: "Mayur Deore", email: "mayur@pelagiamarine.com", role: Role.MANNING }, + { employeeId: "PMS-008", name: "Nikita Accounts", email: "nikita.m@pelagiamarine.com", role: Role.ACCOUNTS }, + { employeeId: "PMS-009", name: "Rakesh Kumar Pandey", email: "rkp@pelagiamarine.com", role: Role.MANAGER }, + { employeeId: "PMS-010", name: "Shailesh B", email: "shailesh.b@pelagiamarine.com", role: Role.ACCOUNTS }, + { employeeId: "PMS-011", name: "Shrikant T", email: "shrikant.t@pelagiamarine.com", role: Role.TECHNICAL }, + { employeeId: "PMS-012", name: "Sunil Gupta", email: "sunil.gupta@pelagiamarine.com", role: Role.MANNING }, + { employeeId: "PMS-013", name: "Supriya Sutar", email: "supriya.s@pelagiamarine.com", role: Role.TECHNICAL }, + { employeeId: "PMS-014", name: "Tajinder Kaur", email: "tajinder.kaur@pelagiamarine.com", role: Role.MANAGER }, +]; + +// ─── Sites ──────────────────────────────────────────────────────────────────── + +const SITES: { code: string; name: string }[] = [ + { code: "HOFC", name: "Head Office" }, + { code: "PMSK", name: "PMS Kochi" }, + { code: "LACD", name: "Laccadives" }, + { code: "HLDA", name: "Haldia" }, + { code: "THKM", name: "Thilakkam" }, + { code: "KVRT", name: "Kavaratti" }, + { code: "THNK", name: "Thinnakara" }, +]; + +// ─── Vessels (code, name, site code) ───────────────────────────────────────── + +const VESSELS: { code: string; name: string; siteCode: string }[] = [ + { code: "HNR1", name: "HNR 1", siteCode: "HLDA" }, + { code: "HNR2", name: "HNR 2", siteCode: "LACD" }, + { code: "HNR3", name: "HNR 3", siteCode: "THKM" }, + { code: "HNR4", name: "HNR 4", siteCode: "THNK" }, + { code: "CHAMPION", name: "Champion", siteCode: "PMSK" }, + { code: "HANUNAM", name: "Hanunam", siteCode: "KVRT" }, + { code: "SEJAL", name: "Sejal", siteCode: "HLDA" }, + { code: "SEJAL2", name: "Sejal 2", siteCode: "LACD" }, + { code: "GD3000", name: "GD 3000", siteCode: "THKM" }, + { code: "THILAKKAM", name: "Thilakkam", siteCode: "THNK" }, +]; + +// ─── Main ───────────────────────────────────────────────────────────────────── + +async function main() { + console.log("🌱 Pelagia production seed starting…\n"); + + // ── Users ────────────────────────────────────────────────────────────────── + console.log("👤 Seeding users…"); + for (const u of USERS) { + await db.user.upsert({ + where: { email: u.email }, + update: { name: u.name, role: u.role }, + create: { + employeeId: u.employeeId, + email: u.email, + name: u.name, + role: u.role, + // No passwordHash — SSO-only login + }, + }); + console.log(` ✓ ${u.name} <${u.email}> [${u.role}]`); + } + + // ── Sites ────────────────────────────────────────────────────────────────── + console.log("\n📍 Seeding sites…"); + const siteIdMap = new Map(); + for (const s of SITES) { + const site = await db.site.upsert({ + where: { code: s.code }, + update: { name: s.name }, + create: { code: s.code, name: s.name }, + }); + siteIdMap.set(s.code, site.id); + console.log(` ✓ ${s.name} (${s.code})`); + } + + // ── Vessels ──────────────────────────────────────────────────────────────── + console.log("\n🚢 Seeding vessels…"); + for (const v of VESSELS) { + const siteId = siteIdMap.get(v.siteCode); + if (!siteId) { + console.warn(` ⚠ Unknown site code "${v.siteCode}" for vessel ${v.code} — skipping`); + continue; + } + await db.vessel.upsert({ + where: { code: v.code }, + update: { name: v.name, siteId }, + create: { code: v.code, name: v.name, siteId }, + }); + console.log(` ✓ ${v.name} (${v.code}) → ${v.siteCode}`); + } + + // ── Accounting Codes ─────────────────────────────────────────────────────── + console.log("\n📊 Seeding accounting codes…"); + + const codeIdMap = new Map(); + + // Pass 1 — upsert every entry without parent links + for (const entry of ACCOUNTING_CODES) { + const rec = await db.account.upsert({ + where: { code: entry.code }, + update: { name: entry.name }, + create: { code: entry.code, name: entry.name }, + }); + codeIdMap.set(entry.code, rec.id); + } + + // Pass 2 — wire up parent relationships + for (const entry of ACCOUNTING_CODES) { + if (entry.parentCode) { + const parentId = codeIdMap.get(entry.parentCode); + if (parentId) { + await db.account.update({ + where: { code: entry.code }, + data: { parentId }, + }); + } + } + } + + const leafCount = ACCOUNTING_CODES.filter((e) => { + return !ACCOUNTING_CODES.some((other) => other.parentCode === e.code); + }).length; + console.log(` ✓ ${ACCOUNTING_CODES.length} codes (${leafCount} selectable leaf items)`); + + console.log("\n✅ Production seed complete."); +} + +main() + .catch((e) => { + console.error("❌ Seed failed:", e); + process.exit(1); + }) + .finally(() => db.$disconnect());