seed-prod.ts: - Correct vessel list: Head Office, PMS Kochi, CSD H&R 1/3/4, CSD Champion, CSD Hanuman, Kavaratti, Laccadives, Thinnakara, Thillaakam, GD3000 - Add System Admin user (admin@pelagia.local / admin1234) via bcrypt Unit tests: - po-import-parser: assert on line item .name rather than .description - po-line-items-editor: fix placeholder text assertions, add .name to LineItemInput fixtures, add two new GST 0% calculation tests - validations: add .name to line item fixtures; update createPoSchema assertions to reference costCentreRef; mark description as optional Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
161 lines
7.3 KiB
TypeScript
161 lines
7.3 KiB
TypeScript
/**
|
|
* 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";
|
|
import bcrypt from "bcryptjs";
|
|
|
|
const hash = (p: string) => bcrypt.hash(p, 12);
|
|
|
|
const db = new PrismaClient();
|
|
|
|
// ─── Users ────────────────────────────────────────────────────────────────────
|
|
|
|
const USERS: { employeeId: string; name: string; email: string; role: Role }[] = [
|
|
{ employeeId: "ACC-001", name: "Akshata Teli", email: "akshata@pelagiamarine.com", role: Role.ACCOUNTS },
|
|
{ employeeId: "ACC-002", name: "Dipali K", email: "dipali.k@pelagiamarine.com", role: Role.ACCOUNTS },
|
|
{ employeeId: "ACC-003", name: "Nikita Accounts", email: "nikita.m@pelagiamarine.com", role: Role.ACCOUNTS },
|
|
{ employeeId: "ACC-004", name: "Shailesh B", email: "shailesh.b@pelagiamarine.com", role: Role.ACCOUNTS },
|
|
{ employeeId: "MGR-001", name: "Chhagan Sarang", email: "chhagan.sarang@pelagiamarine.com", role: Role.MANAGER },
|
|
{ employeeId: "MGR-002", name: "Kaushal Pal Singh", email: "kps@pelagiamarine.com", role: Role.MANAGER },
|
|
{ employeeId: "MGR-003", name: "Rakesh Kumar Pandey", email: "rkp@pelagiamarine.com", role: Role.MANAGER },
|
|
{ employeeId: "MGR-004", name: "Tajinder Kaur", email: "tajinder.kaur@pelagiamarine.com", role: Role.MANAGER },
|
|
{ employeeId: "MAN-001", name: "Mayur Deore", email: "mayur@pelagiamarine.com", role: Role.MANNING },
|
|
{ employeeId: "MAN-002", name: "Sunil Gupta", email: "sunil.gupta@pelagiamarine.com", role: Role.MANNING },
|
|
{ employeeId: "TCH-001", name: "Eeshan Singh", email: "eeshan.singh@pelagiamarine.com", role: Role.TECHNICAL },
|
|
{ employeeId: "TCH-002", name: "Manjuprasad B", email: "manjuprasad.b@pelagiamarine.com", role: Role.TECHNICAL },
|
|
{ employeeId: "TCH-003", name: "Shrikant T", email: "shrikant.t@pelagiamarine.com", role: Role.TECHNICAL },
|
|
{ employeeId: "TCH-004", name: "Supriya Sutar", email: "supriya.s@pelagiamarine.com", role: Role.TECHNICAL },
|
|
];
|
|
|
|
// ─── 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 }[] = [
|
|
{ name: "Head Office", code: "HOFC" },
|
|
{ name: "CSD PMS KOCHI", code: "PMSK" },
|
|
{ name: "CSD H&R 1", code: "HNR1" },
|
|
{ name: "CSD H&R 3", code: "HNR3" },
|
|
{ name: "CSD H&R 4", code: "HNR4" },
|
|
{ name: "CSD CHAMPION", code: "CHMP" },
|
|
{ name: "CSD HANUMAN", code: "HANU" },
|
|
{ name: "KAVARATTI", code: "KVRT" },
|
|
{ name: "LACCADIVES", code: "LACD" },
|
|
{ name: "THINNAKARA", code: "THNK" },
|
|
{ name: "THILLAAKAM", code: "THKM" },
|
|
{ name: "GD3000", code: "GD30" },
|
|
];
|
|
|
|
// ─── 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}]`);
|
|
}
|
|
|
|
const admin = await db.user.upsert({
|
|
where: { email: "admin@pelagia.local" },
|
|
update: {},
|
|
create: { employeeId: "ADM-001", email: "admin@pelagia.local", name: "System Admin", passwordHash: await hash("admin1234"), role: Role.ADMIN },
|
|
});
|
|
|
|
// ── Sites ──────────────────────────────────────────────────────────────────
|
|
console.log("\n📍 Seeding sites…");
|
|
for (const s of SITES) {
|
|
await db.site.upsert({
|
|
where: { code: s.code },
|
|
update: { name: s.name },
|
|
create: { code: s.code, name: s.name },
|
|
});
|
|
console.log(` ✓ ${s.name} (${s.code})`);
|
|
}
|
|
|
|
// ── Vessels ────────────────────────────────────────────────────────────────
|
|
console.log("\n🚢 Seeding vessels…");
|
|
for (const v of VESSELS) {
|
|
await db.vessel.upsert({
|
|
where: { code: v.code },
|
|
update: { name: v.name },
|
|
create: { code: v.code, name: v.name },
|
|
});
|
|
console.log(` ✓ ${v.name} (${v.code})`);
|
|
}
|
|
|
|
// ── Accounting Codes ───────────────────────────────────────────────────────
|
|
console.log("\n📊 Seeding accounting codes…");
|
|
|
|
const codeIdMap = new Map<string, string>();
|
|
|
|
// 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());
|