1759 lines
78 KiB
TypeScript
1759 lines
78 KiB
TypeScript
import { PrismaClient, AccountType, RoleType, FiscalPeriodStatus, InvoiceStatus } from "@prisma/client";
|
||
import bcrypt from "bcryptjs";
|
||
|
||
const prisma = new PrismaClient();
|
||
|
||
async function main() {
|
||
console.log("🌱 Starting database seed...");
|
||
|
||
// Create Organization
|
||
console.log("📊 Creating organization...");
|
||
const organization = await prisma.organization.create({
|
||
data: {
|
||
name: "Acme Holdings",
|
||
legalName: "Acme Holdings Inc.",
|
||
fiscalYearStartMonth: 1,
|
||
fiscalYearStartDay: 1,
|
||
defaultCurrency: "USD",
|
||
timezone: "America/New_York",
|
||
},
|
||
});
|
||
console.log(`✓ Organization created: ${organization.name}`);
|
||
|
||
// Create Entities
|
||
console.log("🏢 Creating entities...");
|
||
const acmeSoftware = await prisma.entity.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
name: "Acme Software Inc",
|
||
code: "ACM-US",
|
||
type: "SUBSIDIARY",
|
||
defaultCurrency: "USD",
|
||
status: "ACTIVE",
|
||
},
|
||
});
|
||
const acmeCloud = await prisma.entity.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
name: "Acme Cloud Services Ltd",
|
||
code: "ACM-UK",
|
||
type: "SUBSIDIARY",
|
||
defaultCurrency: "GBP",
|
||
status: "ACTIVE",
|
||
},
|
||
});
|
||
const acmeEurope = await prisma.entity.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
name: "Acme Europe GmbH",
|
||
code: "ACM-DE",
|
||
type: "SUBSIDIARY",
|
||
defaultCurrency: "EUR",
|
||
status: "ACTIVE",
|
||
},
|
||
});
|
||
const acmePacific = await prisma.entity.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
name: "Acme Pacific Pty Ltd",
|
||
code: "ACM-AU",
|
||
type: "SUBSIDIARY",
|
||
defaultCurrency: "AUD",
|
||
status: "ACTIVE",
|
||
},
|
||
});
|
||
const entities = [acmeSoftware, acmeCloud, acmeEurope, acmePacific];
|
||
console.log(`✓ Created ${entities.length} entities`);
|
||
|
||
// Create Roles
|
||
console.log("👥 Creating roles...");
|
||
const roleConfigs: { name: string; roleType: RoleType }[] = [
|
||
{ name: "CEO", roleType: "CEO" },
|
||
{ name: "CFO", roleType: "CFO" },
|
||
{ name: "Controller", roleType: "CONTROLLER" },
|
||
{ name: "Manager", roleType: "MANAGER" },
|
||
{ name: "Analyst", roleType: "ANALYST" },
|
||
{ name: "Clerk", roleType: "CLERK" },
|
||
];
|
||
const roles = [];
|
||
for (const rc of roleConfigs) {
|
||
const role = await prisma.role.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
name: rc.name,
|
||
roleType: rc.roleType,
|
||
description: `${rc.name} role`,
|
||
isSystem: true,
|
||
},
|
||
});
|
||
roles.push(role);
|
||
}
|
||
console.log(`✓ Created ${roles.length} roles`);
|
||
|
||
// Create Admin User
|
||
console.log("👤 Creating admin user...");
|
||
const hashedPassword = await bcrypt.hash("demo123", 10);
|
||
const user = await prisma.user.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
email: "ryan@acme.com",
|
||
passwordHash: hashedPassword,
|
||
name: "Ryan Francis",
|
||
firstName: "Ryan",
|
||
lastName: "Francis",
|
||
status: "ACTIVE",
|
||
},
|
||
});
|
||
// Assign CEO role
|
||
await prisma.userRole.create({
|
||
data: {
|
||
userId: user.id,
|
||
roleId: roles[0].id,
|
||
},
|
||
});
|
||
console.log(`✓ Admin user created: ${user.email}`);
|
||
|
||
// Create Chart of Accounts
|
||
console.log("📈 Creating chart of accounts...");
|
||
const accountsData: { code: string; name: string; type: AccountType; parent: string | null }[] = [
|
||
{ code: "1000", name: "Cash and Cash Equivalents", type: "ASSET", parent: null },
|
||
{ code: "1010", name: "Operating Account", type: "ASSET", parent: "1000" },
|
||
{ code: "1020", name: "Payroll Account", type: "ASSET", parent: "1000" },
|
||
{ code: "1100", name: "Accounts Receivable", type: "ASSET", parent: null },
|
||
{ code: "1200", name: "Prepaid Expenses", type: "ASSET", parent: null },
|
||
{ code: "1500", name: "Fixed Assets", type: "ASSET", parent: null },
|
||
{ code: "1550", name: "Accumulated Depreciation", type: "ASSET", parent: null },
|
||
{ code: "2000", name: "Accounts Payable", type: "LIABILITY", parent: null },
|
||
{ code: "2100", name: "Deferred Revenue", type: "LIABILITY", parent: null },
|
||
{ code: "2200", name: "Accrued Liabilities", type: "LIABILITY", parent: null },
|
||
{ code: "2500", name: "Long-Term Debt", type: "LIABILITY", parent: null },
|
||
{ code: "3000", name: "Common Stock", type: "EQUITY", parent: null },
|
||
{ code: "3100", name: "Additional Paid-In Capital", type: "EQUITY", parent: null },
|
||
{ code: "3200", name: "Retained Earnings", type: "EQUITY", parent: null },
|
||
{ code: "4000", name: "Software Revenue", type: "REVENUE", parent: null },
|
||
{ code: "4100", name: "Services Revenue", type: "REVENUE", parent: null },
|
||
{ code: "4500", name: "Interest Income", type: "REVENUE", parent: null },
|
||
{ code: "5000", name: "Cost of Goods Sold", type: "EXPENSE", parent: null },
|
||
{ code: "6000", name: "Operating Expenses", type: "EXPENSE", parent: null },
|
||
{ code: "6100", name: "Sales & Marketing", type: "EXPENSE", parent: "6000" },
|
||
{ code: "6200", name: "Engineering", type: "EXPENSE", parent: "6000" },
|
||
{ code: "6300", name: "General & Administrative", type: "EXPENSE", parent: "6000" },
|
||
{ code: "6400", name: "IT & Infrastructure", type: "EXPENSE", parent: "6000" },
|
||
{ code: "7000", name: "Interest Expense", type: "EXPENSE", parent: null },
|
||
];
|
||
|
||
// First pass: create all accounts without parents
|
||
const accounts: Record<string, any> = {};
|
||
for (const a of accountsData) {
|
||
const account = await prisma.chartOfAccount.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
accountCode: a.code,
|
||
accountName: a.name,
|
||
accountType: a.type,
|
||
status: "ACTIVE",
|
||
level: a.parent ? 2 : 1,
|
||
hierarchy: a.parent ? `${a.parent}/${a.code}` : a.code,
|
||
},
|
||
});
|
||
accounts[a.code] = account;
|
||
}
|
||
|
||
// Second pass: set parent relationships
|
||
for (const a of accountsData) {
|
||
if (a.parent && accounts[a.parent]) {
|
||
await prisma.chartOfAccount.update({
|
||
where: { id: accounts[a.code].id },
|
||
data: { parentAccountId: accounts[a.parent].id },
|
||
});
|
||
}
|
||
}
|
||
console.log(`✓ Created ${accountsData.length} accounts`);
|
||
|
||
// ========================================
|
||
// Create Fiscal Periods for FY2024, FY2025, FY2026
|
||
// ========================================
|
||
console.log("📅 Creating fiscal periods...");
|
||
const months = [
|
||
"January", "February", "March", "April", "May", "June",
|
||
"July", "August", "September", "October", "November", "December",
|
||
];
|
||
|
||
const allPeriods: Record<string, any> = {};
|
||
|
||
// FY2024 — all CLOSED
|
||
for (let i = 0; i < 12; i++) {
|
||
const startDate = new Date(2024, i, 1);
|
||
const endDate = new Date(2024, i + 1, 0);
|
||
const period = await prisma.fiscalPeriod.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
fiscalYear: 2024,
|
||
periodNumber: i + 1,
|
||
periodName: `${months[i]} 2024`,
|
||
startDate,
|
||
endDate,
|
||
status: "CLOSED" as FiscalPeriodStatus,
|
||
},
|
||
});
|
||
allPeriods[`2024-${i + 1}`] = period;
|
||
}
|
||
|
||
// FY2025 — all CLOSED (year is complete)
|
||
for (let i = 0; i < 12; i++) {
|
||
const startDate = new Date(2025, i, 1);
|
||
const endDate = new Date(2025, i + 1, 0);
|
||
const period = await prisma.fiscalPeriod.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
fiscalYear: 2025,
|
||
periodNumber: i + 1,
|
||
periodName: `${months[i]} 2025`,
|
||
startDate,
|
||
endDate,
|
||
status: "CLOSED" as FiscalPeriodStatus,
|
||
},
|
||
});
|
||
allPeriods[`2025-${i + 1}`] = period;
|
||
}
|
||
|
||
// FY2026 — Jan–Mar CLOSED, Apr–Dec OPEN (current date: April 5, 2026)
|
||
for (let i = 0; i < 12; i++) {
|
||
const startDate = new Date(2026, i, 1);
|
||
const endDate = new Date(2026, i + 1, 0);
|
||
const status: FiscalPeriodStatus = i < 3 ? "CLOSED" : "OPEN";
|
||
const period = await prisma.fiscalPeriod.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
fiscalYear: 2026,
|
||
periodNumber: i + 1,
|
||
periodName: `${months[i]} 2026`,
|
||
startDate,
|
||
endDate,
|
||
status,
|
||
},
|
||
});
|
||
allPeriods[`2026-${i + 1}`] = period;
|
||
}
|
||
console.log(`✓ Created 36 fiscal periods (FY2024-FY2026)`);
|
||
|
||
// ========================================
|
||
// COMPREHENSIVE JOURNAL ENTRIES
|
||
// Spanning Jan 2025 – March 2026 across all account types
|
||
// ========================================
|
||
console.log("📝 Creating comprehensive journal entries...");
|
||
let jeCounter = 0;
|
||
|
||
async function createJE(opts: {
|
||
periodKey: string;
|
||
date: string;
|
||
description: string;
|
||
sourceModule: string;
|
||
status: "POSTED" | "DRAFT" | "PENDING_APPROVAL";
|
||
entityRef: any;
|
||
lines: { accountCode: string; debit: number; credit: number; desc: string }[];
|
||
}) {
|
||
jeCounter++;
|
||
const entryNumber = `JE-${opts.date.slice(0, 4)}-${String(jeCounter).padStart(4, "0")}`;
|
||
const totalDebits = opts.lines.reduce((s, l) => s + l.debit, 0);
|
||
const totalCredits = opts.lines.reduce((s, l) => s + l.credit, 0);
|
||
const isPosted = opts.status === "POSTED";
|
||
const entryDate = new Date(opts.date);
|
||
|
||
return prisma.journalEntry.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
fiscalPeriodId: allPeriods[opts.periodKey].id,
|
||
entryNumber,
|
||
entryDate,
|
||
description: opts.description,
|
||
sourceModule: opts.sourceModule,
|
||
status: opts.status,
|
||
totalDebits,
|
||
totalCredits,
|
||
isBalanced: totalDebits === totalCredits,
|
||
postedAt: isPosted ? entryDate : null,
|
||
postedBy: isPosted ? user.id : null,
|
||
createdBy: user.name,
|
||
journalEntryLines: {
|
||
create: opts.lines.map((l, idx) => ({
|
||
organizationId: organization.id,
|
||
entityId: opts.entityRef.id,
|
||
accountId: accounts[l.accountCode].id,
|
||
debitAmount: l.debit,
|
||
creditAmount: l.credit,
|
||
lineNumber: idx + 1,
|
||
description: l.desc,
|
||
})),
|
||
},
|
||
},
|
||
});
|
||
}
|
||
|
||
// ---- FY2025 MONTHLY JOURNAL ENTRIES ----
|
||
// We'll create recurring monthly entries + some one-off transactions
|
||
|
||
for (let month = 1; month <= 12; month++) {
|
||
const mm = String(month).padStart(2, "0");
|
||
const periodKey = `2025-${month}`;
|
||
const monthName = months[month - 1];
|
||
|
||
// 1) Monthly Software Revenue (ACM-US) — DR AR, CR Revenue
|
||
await createJE({
|
||
periodKey,
|
||
date: `2025-${mm}-05`,
|
||
description: `${monthName} 2025 software license revenue`,
|
||
sourceModule: "AR",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "1100", debit: 185000, credit: 0, desc: `AR - software licenses ${monthName}` },
|
||
{ accountCode: "4000", debit: 0, credit: 185000, desc: `Software revenue ${monthName}` },
|
||
],
|
||
});
|
||
|
||
// 2) Monthly Services Revenue (ACM-US) — DR AR, CR Services Revenue
|
||
await createJE({
|
||
periodKey,
|
||
date: `2025-${mm}-08`,
|
||
description: `${monthName} 2025 consulting & services revenue`,
|
||
sourceModule: "AR",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "1100", debit: 92000, credit: 0, desc: `AR - consulting services ${monthName}` },
|
||
{ accountCode: "4100", debit: 0, credit: 92000, desc: `Services revenue ${monthName}` },
|
||
],
|
||
});
|
||
|
||
// 3) Monthly Cash Collection (ACM-US) — DR Cash, CR AR
|
||
await createJE({
|
||
periodKey,
|
||
date: `2025-${mm}-20`,
|
||
description: `${monthName} 2025 customer payments received`,
|
||
sourceModule: "AR",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "1010", debit: 245000, credit: 0, desc: `Cash received from customers ${monthName}` },
|
||
{ accountCode: "1100", debit: 0, credit: 245000, desc: `AR collections ${monthName}` },
|
||
],
|
||
});
|
||
|
||
// 4) Monthly COGS (ACM-US) — DR COGS, CR Cash
|
||
await createJE({
|
||
periodKey,
|
||
date: `2025-${mm}-10`,
|
||
description: `${monthName} 2025 cost of goods sold`,
|
||
sourceModule: "GL",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "5000", debit: 55000, credit: 0, desc: `COGS - hosting & delivery ${monthName}` },
|
||
{ accountCode: "1010", debit: 0, credit: 55000, desc: `Payment for COGS ${monthName}` },
|
||
],
|
||
});
|
||
|
||
// 5) Monthly Engineering Payroll (ACM-US) — DR Engineering, CR Payroll Account
|
||
await createJE({
|
||
periodKey,
|
||
date: `2025-${mm}-25`,
|
||
description: `${monthName} 2025 engineering payroll`,
|
||
sourceModule: "PAYROLL",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "6200", debit: 210000, credit: 0, desc: `Engineering salaries ${monthName}` },
|
||
{ accountCode: "1020", debit: 0, credit: 210000, desc: `Payroll disbursement ${monthName}` },
|
||
],
|
||
});
|
||
|
||
// 6) Monthly S&M Expenses — DR S&M, CR AP
|
||
await createJE({
|
||
periodKey,
|
||
date: `2025-${mm}-12`,
|
||
description: `${monthName} 2025 sales & marketing expenses`,
|
||
sourceModule: "AP",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "6100", debit: 45000, credit: 0, desc: `Marketing campaigns & sales ops ${monthName}` },
|
||
{ accountCode: "2000", debit: 0, credit: 45000, desc: `AP - marketing vendors ${monthName}` },
|
||
],
|
||
});
|
||
|
||
// 7) Monthly G&A Expenses — DR G&A, CR AP
|
||
await createJE({
|
||
periodKey,
|
||
date: `2025-${mm}-15`,
|
||
description: `${monthName} 2025 general & administrative`,
|
||
sourceModule: "AP",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "6300", debit: 38000, credit: 0, desc: `Office rent, insurance, admin ${monthName}` },
|
||
{ accountCode: "2000", debit: 0, credit: 38000, desc: `AP - G&A vendors ${monthName}` },
|
||
],
|
||
});
|
||
|
||
// 8) Monthly IT & Infrastructure — DR IT, CR AP
|
||
await createJE({
|
||
periodKey,
|
||
date: `2025-${mm}-14`,
|
||
description: `${monthName} 2025 IT & infrastructure`,
|
||
sourceModule: "AP",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "6400", debit: 22000, credit: 0, desc: `Cloud hosting, SaaS tools ${monthName}` },
|
||
{ accountCode: "2000", debit: 0, credit: 22000, desc: `AP - IT vendors ${monthName}` },
|
||
],
|
||
});
|
||
|
||
// 9) Monthly AP Payments — DR AP, CR Cash (pay last month's bills)
|
||
await createJE({
|
||
periodKey,
|
||
date: `2025-${mm}-18`,
|
||
description: `${monthName} 2025 vendor payments`,
|
||
sourceModule: "AP",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "2000", debit: 95000, credit: 0, desc: `AP payments to vendors ${monthName}` },
|
||
{ accountCode: "1010", debit: 0, credit: 95000, desc: `Cash disbursed ${monthName}` },
|
||
],
|
||
});
|
||
|
||
// 10) Monthly Depreciation — DR Depreciation Expense (G&A), CR Accumulated Depreciation
|
||
await createJE({
|
||
periodKey,
|
||
date: `2025-${mm}-28`,
|
||
description: `${monthName} 2025 depreciation expense`,
|
||
sourceModule: "GL",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "6300", debit: 8500, credit: 0, desc: `Monthly depreciation ${monthName}` },
|
||
{ accountCode: "1550", debit: 0, credit: 8500, desc: `Accumulated depreciation ${monthName}` },
|
||
],
|
||
});
|
||
|
||
// 11) Monthly Interest on Long-Term Debt — DR Interest Expense, CR Accrued Liabilities
|
||
await createJE({
|
||
periodKey,
|
||
date: `2025-${mm}-28`,
|
||
description: `${monthName} 2025 interest expense on long-term debt`,
|
||
sourceModule: "GL",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "7000", debit: 4200, credit: 0, desc: `Interest on term loan ${monthName}` },
|
||
{ accountCode: "2200", debit: 0, credit: 4200, desc: `Accrued interest ${monthName}` },
|
||
],
|
||
});
|
||
|
||
// 12) Monthly Interest Income — DR Cash, CR Interest Income (small)
|
||
await createJE({
|
||
periodKey,
|
||
date: `2025-${mm}-28`,
|
||
description: `${monthName} 2025 interest income earned`,
|
||
sourceModule: "GL",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "1010", debit: 1800, credit: 0, desc: `Interest earned on deposits ${monthName}` },
|
||
{ accountCode: "4500", debit: 0, credit: 1800, desc: `Interest income ${monthName}` },
|
||
],
|
||
});
|
||
|
||
// UK Entity Revenue (quarterly — Q1, Q2, Q3, Q4)
|
||
if (month % 3 === 0) {
|
||
await createJE({
|
||
periodKey,
|
||
date: `2025-${mm}-15`,
|
||
description: `Q${month / 3} 2025 Acme Cloud UK revenue`,
|
||
sourceModule: "AR",
|
||
status: "POSTED",
|
||
entityRef: acmeCloud,
|
||
lines: [
|
||
{ accountCode: "1100", debit: 125000, credit: 0, desc: `AR - UK cloud services Q${month / 3}` },
|
||
{ accountCode: "4000", debit: 0, credit: 125000, desc: `UK cloud revenue Q${month / 3}` },
|
||
],
|
||
});
|
||
}
|
||
|
||
// Europe Entity Revenue (quarterly)
|
||
if (month % 3 === 0) {
|
||
await createJE({
|
||
periodKey,
|
||
date: `2025-${mm}-16`,
|
||
description: `Q${month / 3} 2025 Acme Europe revenue`,
|
||
sourceModule: "AR",
|
||
status: "POSTED",
|
||
entityRef: acmeEurope,
|
||
lines: [
|
||
{ accountCode: "1100", debit: 95000, credit: 0, desc: `AR - Europe consulting Q${month / 3}` },
|
||
{ accountCode: "4100", debit: 0, credit: 95000, desc: `Europe services revenue Q${month / 3}` },
|
||
],
|
||
});
|
||
}
|
||
|
||
// Pacific Entity Revenue (quarterly)
|
||
if (month % 3 === 0) {
|
||
await createJE({
|
||
periodKey,
|
||
date: `2025-${mm}-17`,
|
||
description: `Q${month / 3} 2025 Acme Pacific revenue`,
|
||
sourceModule: "AR",
|
||
status: "POSTED",
|
||
entityRef: acmePacific,
|
||
lines: [
|
||
{ accountCode: "1100", debit: 68000, credit: 0, desc: `AR - Pacific software Q${month / 3}` },
|
||
{ accountCode: "4000", debit: 0, credit: 68000, desc: `Pacific software revenue Q${month / 3}` },
|
||
],
|
||
});
|
||
}
|
||
}
|
||
|
||
// ---- ONE-OFF FY2025 ENTRIES ----
|
||
|
||
// Fixed Asset Purchase (Q1 2025) — DR Fixed Assets, CR Cash
|
||
await createJE({
|
||
periodKey: "2025-2",
|
||
date: "2025-02-15",
|
||
description: "Server hardware and office equipment purchase",
|
||
sourceModule: "AP",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "1500", debit: 350000, credit: 0, desc: "Server hardware, networking gear" },
|
||
{ accountCode: "1010", debit: 0, credit: 350000, desc: "Cash payment for fixed assets" },
|
||
],
|
||
});
|
||
|
||
// Additional Fixed Asset Purchase (Q3 2025)
|
||
await createJE({
|
||
periodKey: "2025-8",
|
||
date: "2025-08-10",
|
||
description: "Office furniture and developer workstations",
|
||
sourceModule: "AP",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "1500", debit: 125000, credit: 0, desc: "Office furniture & workstations" },
|
||
{ accountCode: "1010", debit: 0, credit: 125000, desc: "Cash payment for equipment" },
|
||
],
|
||
});
|
||
|
||
// Prepaid Expenses — annual insurance (Jan 2025)
|
||
await createJE({
|
||
periodKey: "2025-1",
|
||
date: "2025-01-05",
|
||
description: "Annual business insurance premium prepayment",
|
||
sourceModule: "GL",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "1200", debit: 96000, credit: 0, desc: "Prepaid D&O and E&O insurance" },
|
||
{ accountCode: "1010", debit: 0, credit: 96000, desc: "Cash payment for insurance" },
|
||
],
|
||
});
|
||
|
||
// Deferred Revenue Recognition (Q2 2025) — large annual license prepaid
|
||
await createJE({
|
||
periodKey: "2025-4",
|
||
date: "2025-04-01",
|
||
description: "Received annual license prepayment from Global Partners",
|
||
sourceModule: "AR",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "1010", debit: 480000, credit: 0, desc: "Cash received - annual prepayment" },
|
||
{ accountCode: "2100", debit: 0, credit: 480000, desc: "Deferred revenue - Global Partners" },
|
||
],
|
||
});
|
||
|
||
// Deferred Revenue Recognition monthly (Q2-Q4, 9 months of $53,333)
|
||
for (let m = 4; m <= 12; m++) {
|
||
const mm = String(m).padStart(2, "0");
|
||
await createJE({
|
||
periodKey: `2025-${m}`,
|
||
date: `2025-${mm}-28`,
|
||
description: `${months[m - 1]} 2025 deferred revenue recognition`,
|
||
sourceModule: "AR",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "2100", debit: 53333, credit: 0, desc: `Deferred rev recognition ${months[m - 1]}` },
|
||
{ accountCode: "4000", debit: 0, credit: 53333, desc: `Revenue recognized ${months[m - 1]}` },
|
||
],
|
||
});
|
||
}
|
||
|
||
// Long-term Debt Drawdown (Mar 2025) — DR Cash, CR Long-Term Debt
|
||
await createJE({
|
||
periodKey: "2025-3",
|
||
date: "2025-03-01",
|
||
description: "Term loan drawdown - growth capital",
|
||
sourceModule: "GL",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "1010", debit: 2000000, credit: 0, desc: "Loan proceeds received" },
|
||
{ accountCode: "2500", debit: 0, credit: 2000000, desc: "Term loan - 5yr facility" },
|
||
],
|
||
});
|
||
|
||
// Equity Contribution (Jan 2025) — Series B round
|
||
await createJE({
|
||
periodKey: "2025-1",
|
||
date: "2025-01-15",
|
||
description: "Series B equity investment received",
|
||
sourceModule: "GL",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "1010", debit: 5000000, credit: 0, desc: "Series B investment proceeds" },
|
||
{ accountCode: "3000", debit: 0, credit: 500000, desc: "Common stock par value" },
|
||
{ accountCode: "3100", debit: 0, credit: 4500000, desc: "Additional paid-in capital" },
|
||
],
|
||
});
|
||
|
||
// Quarterly debt principal repayment
|
||
for (const q of [6, 9, 12]) {
|
||
const mm = String(q).padStart(2, "0");
|
||
await createJE({
|
||
periodKey: `2025-${q}`,
|
||
date: `2025-${mm}-30`,
|
||
description: `Q${q / 3} 2025 loan principal repayment`,
|
||
sourceModule: "GL",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "2500", debit: 100000, credit: 0, desc: `Term loan principal Q${q / 3}` },
|
||
{ accountCode: "1010", debit: 0, credit: 100000, desc: `Cash - loan repayment Q${q / 3}` },
|
||
],
|
||
});
|
||
}
|
||
|
||
// Payroll Account Funding (monthly — DR Payroll, CR Operating)
|
||
for (let month = 1; month <= 12; month++) {
|
||
const mm = String(month).padStart(2, "0");
|
||
await createJE({
|
||
periodKey: `2025-${month}`,
|
||
date: `2025-${mm}-22`,
|
||
description: `${months[month - 1]} 2025 payroll account funding`,
|
||
sourceModule: "GL",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "1020", debit: 210000, credit: 0, desc: `Payroll funding ${months[month - 1]}` },
|
||
{ accountCode: "1010", debit: 0, credit: 210000, desc: `Transfer to payroll ${months[month - 1]}` },
|
||
],
|
||
});
|
||
}
|
||
|
||
// ---- FY2026 Q1 JOURNAL ENTRIES (Jan–Mar 2026) ----
|
||
for (let month = 1; month <= 3; month++) {
|
||
const mm = String(month).padStart(2, "0");
|
||
const periodKey = `2026-${month}`;
|
||
const monthName = months[month - 1];
|
||
|
||
// Revenue entries (growing 10% over FY2025)
|
||
await createJE({
|
||
periodKey,
|
||
date: `2026-${mm}-05`,
|
||
description: `${monthName} 2026 software license revenue`,
|
||
sourceModule: "AR",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "1100", debit: 203500, credit: 0, desc: `AR - software licenses ${monthName}` },
|
||
{ accountCode: "4000", debit: 0, credit: 203500, desc: `Software revenue ${monthName}` },
|
||
],
|
||
});
|
||
|
||
await createJE({
|
||
periodKey,
|
||
date: `2026-${mm}-08`,
|
||
description: `${monthName} 2026 services revenue`,
|
||
sourceModule: "AR",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "1100", debit: 101200, credit: 0, desc: `AR - consulting services ${monthName}` },
|
||
{ accountCode: "4100", debit: 0, credit: 101200, desc: `Services revenue ${monthName}` },
|
||
],
|
||
});
|
||
|
||
// Cash collections
|
||
await createJE({
|
||
periodKey,
|
||
date: `2026-${mm}-20`,
|
||
description: `${monthName} 2026 customer payments received`,
|
||
sourceModule: "AR",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "1010", debit: 270000, credit: 0, desc: `Cash received ${monthName}` },
|
||
{ accountCode: "1100", debit: 0, credit: 270000, desc: `AR collections ${monthName}` },
|
||
],
|
||
});
|
||
|
||
// COGS
|
||
await createJE({
|
||
periodKey,
|
||
date: `2026-${mm}-10`,
|
||
description: `${monthName} 2026 cost of goods sold`,
|
||
sourceModule: "GL",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "5000", debit: 60500, credit: 0, desc: `COGS ${monthName}` },
|
||
{ accountCode: "1010", debit: 0, credit: 60500, desc: `COGS payment ${monthName}` },
|
||
],
|
||
});
|
||
|
||
// Engineering Payroll
|
||
await createJE({
|
||
periodKey,
|
||
date: `2026-${mm}-25`,
|
||
description: `${monthName} 2026 engineering payroll`,
|
||
sourceModule: "PAYROLL",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "6200", debit: 225000, credit: 0, desc: `Engineering salaries ${monthName}` },
|
||
{ accountCode: "1020", debit: 0, credit: 225000, desc: `Payroll disbursement ${monthName}` },
|
||
],
|
||
});
|
||
|
||
// S&M
|
||
await createJE({
|
||
periodKey,
|
||
date: `2026-${mm}-12`,
|
||
description: `${monthName} 2026 sales & marketing`,
|
||
sourceModule: "AP",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "6100", debit: 49500, credit: 0, desc: `Marketing ${monthName}` },
|
||
{ accountCode: "2000", debit: 0, credit: 49500, desc: `AP - marketing ${monthName}` },
|
||
],
|
||
});
|
||
|
||
// G&A
|
||
await createJE({
|
||
periodKey,
|
||
date: `2026-${mm}-15`,
|
||
description: `${monthName} 2026 G&A expenses`,
|
||
sourceModule: "AP",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "6300", debit: 41800, credit: 0, desc: `G&A expenses ${monthName}` },
|
||
{ accountCode: "2000", debit: 0, credit: 41800, desc: `AP - G&A ${monthName}` },
|
||
],
|
||
});
|
||
|
||
// IT
|
||
await createJE({
|
||
periodKey,
|
||
date: `2026-${mm}-14`,
|
||
description: `${monthName} 2026 IT & infrastructure`,
|
||
sourceModule: "AP",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "6400", debit: 24200, credit: 0, desc: `IT costs ${monthName}` },
|
||
{ accountCode: "2000", debit: 0, credit: 24200, desc: `AP - IT ${monthName}` },
|
||
],
|
||
});
|
||
|
||
// AP Payments
|
||
await createJE({
|
||
periodKey,
|
||
date: `2026-${mm}-18`,
|
||
description: `${monthName} 2026 vendor payments`,
|
||
sourceModule: "AP",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "2000", debit: 105000, credit: 0, desc: `AP payments ${monthName}` },
|
||
{ accountCode: "1010", debit: 0, credit: 105000, desc: `Cash disbursed ${monthName}` },
|
||
],
|
||
});
|
||
|
||
// Depreciation
|
||
await createJE({
|
||
periodKey,
|
||
date: `2026-${mm}-28`,
|
||
description: `${monthName} 2026 depreciation`,
|
||
sourceModule: "GL",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "6300", debit: 9900, credit: 0, desc: `Depreciation ${monthName}` },
|
||
{ accountCode: "1550", debit: 0, credit: 9900, desc: `Accum depreciation ${monthName}` },
|
||
],
|
||
});
|
||
|
||
// Interest Expense
|
||
await createJE({
|
||
periodKey,
|
||
date: `2026-${mm}-28`,
|
||
description: `${monthName} 2026 interest expense`,
|
||
sourceModule: "GL",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "7000", debit: 3500, credit: 0, desc: `Interest on debt ${monthName}` },
|
||
{ accountCode: "2200", debit: 0, credit: 3500, desc: `Accrued interest ${monthName}` },
|
||
],
|
||
});
|
||
|
||
// Interest Income
|
||
await createJE({
|
||
periodKey,
|
||
date: `2026-${mm}-28`,
|
||
description: `${monthName} 2026 interest income`,
|
||
sourceModule: "GL",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "1010", debit: 2100, credit: 0, desc: `Interest earned ${monthName}` },
|
||
{ accountCode: "4500", debit: 0, credit: 2100, desc: `Interest income ${monthName}` },
|
||
],
|
||
});
|
||
|
||
// Payroll funding
|
||
await createJE({
|
||
periodKey,
|
||
date: `2026-${mm}-22`,
|
||
description: `${monthName} 2026 payroll account funding`,
|
||
sourceModule: "GL",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "1020", debit: 225000, credit: 0, desc: `Payroll funding ${monthName}` },
|
||
{ accountCode: "1010", debit: 0, credit: 225000, desc: `Transfer to payroll ${monthName}` },
|
||
],
|
||
});
|
||
}
|
||
|
||
// Q1 2026 subsidiary revenues
|
||
await createJE({
|
||
periodKey: "2026-3",
|
||
date: "2026-03-15",
|
||
description: "Q1 2026 Acme Cloud UK revenue",
|
||
sourceModule: "AR",
|
||
status: "POSTED",
|
||
entityRef: acmeCloud,
|
||
lines: [
|
||
{ accountCode: "1100", debit: 142000, credit: 0, desc: "AR - UK cloud services Q1 2026" },
|
||
{ accountCode: "4000", debit: 0, credit: 142000, desc: "UK cloud revenue Q1 2026" },
|
||
],
|
||
});
|
||
await createJE({
|
||
periodKey: "2026-3",
|
||
date: "2026-03-16",
|
||
description: "Q1 2026 Acme Europe revenue",
|
||
sourceModule: "AR",
|
||
status: "POSTED",
|
||
entityRef: acmeEurope,
|
||
lines: [
|
||
{ accountCode: "1100", debit: 108000, credit: 0, desc: "AR - Europe consulting Q1 2026" },
|
||
{ accountCode: "4100", debit: 0, credit: 108000, desc: "Europe services revenue Q1 2026" },
|
||
],
|
||
});
|
||
await createJE({
|
||
periodKey: "2026-3",
|
||
date: "2026-03-17",
|
||
description: "Q1 2026 Acme Pacific revenue",
|
||
sourceModule: "AR",
|
||
status: "POSTED",
|
||
entityRef: acmePacific,
|
||
lines: [
|
||
{ accountCode: "1100", debit: 78000, credit: 0, desc: "AR - Pacific software Q1 2026" },
|
||
{ accountCode: "4000", debit: 0, credit: 78000, desc: "Pacific revenue Q1 2026" },
|
||
],
|
||
});
|
||
|
||
// FY2026 Opening retained earnings entry (close FY2025 net income)
|
||
// FY2025 Net Income calc: Revenue (185k*12+92k*12+125k*4+95k*4+68k*4+53.3k*9+1.8k*12) - Expenses
|
||
// Approx: Revenue ~$4.87M, Expenses ~$4.61M → Net income ~$260k → round to closing entry
|
||
await createJE({
|
||
periodKey: "2026-1",
|
||
date: "2026-01-01",
|
||
description: "FY2025 year-end close - retained earnings",
|
||
sourceModule: "GL",
|
||
status: "POSTED",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "3200", debit: 0, credit: 258400, desc: "FY2025 net income to retained earnings" },
|
||
{ accountCode: "1010", debit: 258400, credit: 0, desc: "Opening balance adjustment" },
|
||
],
|
||
});
|
||
|
||
// A couple DRAFT entries in April 2026 (current month)
|
||
await createJE({
|
||
periodKey: "2026-4",
|
||
date: "2026-04-03",
|
||
description: "April 2026 software revenue accrual (draft)",
|
||
sourceModule: "AR",
|
||
status: "DRAFT",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "1100", debit: 203500, credit: 0, desc: "AR - April software" },
|
||
{ accountCode: "4000", debit: 0, credit: 203500, desc: "April software revenue" },
|
||
],
|
||
});
|
||
|
||
await createJE({
|
||
periodKey: "2026-4",
|
||
date: "2026-04-04",
|
||
description: "April 2026 payroll accrual (pending approval)",
|
||
sourceModule: "PAYROLL",
|
||
status: "PENDING_APPROVAL",
|
||
entityRef: acmeSoftware,
|
||
lines: [
|
||
{ accountCode: "6200", debit: 225000, credit: 0, desc: "April engineering payroll" },
|
||
{ accountCode: "2200", debit: 0, credit: 225000, desc: "Accrued payroll" },
|
||
],
|
||
});
|
||
|
||
console.log(`✓ Created ${jeCounter} journal entries`);
|
||
|
||
// ========================================
|
||
// VENDORS
|
||
// ========================================
|
||
console.log("🏪 Creating vendors...");
|
||
const vendors = [];
|
||
const vendorData = [
|
||
{ code: "V001", name: "Amazon Web Services", termDays: 30, type: "Cloud Services", email: "billing@aws.amazon.com", phone: "206-555-0100" },
|
||
{ code: "V002", name: "Salesforce Inc", termDays: 45, type: "Software", email: "invoices@salesforce.com", phone: "415-555-0200" },
|
||
{ code: "V003", name: "WeWork", termDays: 30, type: "Real Estate", email: "accounts@wework.com", phone: "212-555-0300" },
|
||
{ code: "V004", name: "ADP Inc", termDays: 15, type: "HR Services", email: "billing@adp.com", phone: "973-555-0400" },
|
||
{ code: "V005", name: "Dell Technologies", termDays: 60, type: "Hardware", email: "orders@dell.com", phone: "512-555-0500" },
|
||
{ code: "V006", name: "Google Cloud Platform", termDays: 30, type: "Cloud Services", email: "billing@google.com", phone: "650-555-0600" },
|
||
{ code: "V007", name: "Datadog Inc", termDays: 30, type: "Software", email: "billing@datadoghq.com", phone: "646-555-0700" },
|
||
{ code: "V008", name: "Marsh McLennan", termDays: 45, type: "Insurance", email: "accounts@marsh.com", phone: "212-555-0800" },
|
||
];
|
||
|
||
for (const v of vendorData) {
|
||
const vendor = await prisma.vendor.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
vendorCode: v.code,
|
||
vendorName: v.name,
|
||
vendorType: v.type,
|
||
paymentTermDays: v.termDays,
|
||
email: v.email,
|
||
phone: v.phone,
|
||
status: "ACTIVE",
|
||
},
|
||
});
|
||
vendors.push(vendor);
|
||
}
|
||
console.log(`✓ Created ${vendors.length} vendors`);
|
||
|
||
// ========================================
|
||
// PURCHASE ORDERS
|
||
// ========================================
|
||
console.log("📦 Creating purchase orders...");
|
||
const purchaseOrders = [];
|
||
const poData = [
|
||
{
|
||
poNumber: "PO-2026-001", vendor: vendors[0], orderDate: new Date("2026-01-15"), expectedDate: new Date("2026-02-01"),
|
||
status: "RECEIVED", subtotal: 22000, tax: 1980,
|
||
lines: [{ lineNumber: 1, description: "AWS Reserved Instances - Q1 2026", quantity: 1, unitPrice: 22000 }],
|
||
},
|
||
{
|
||
poNumber: "PO-2026-002", vendor: vendors[1], orderDate: new Date("2026-01-20"), expectedDate: new Date("2026-02-15"),
|
||
status: "RECEIVED", subtotal: 48000, tax: 4320,
|
||
lines: [{ lineNumber: 1, description: "Salesforce Enterprise - Annual Renewal", quantity: 1, unitPrice: 48000 }],
|
||
},
|
||
{
|
||
poNumber: "PO-2026-003", vendor: vendors[2], orderDate: new Date("2026-02-01"), expectedDate: new Date("2026-03-01"),
|
||
status: "APPROVED", subtotal: 30000, tax: 2700,
|
||
lines: [
|
||
{ lineNumber: 1, description: "Office lease - Q2 2026", quantity: 3, unitPrice: 8000 },
|
||
{ lineNumber: 2, description: "Common area + utilities", quantity: 1, unitPrice: 6000 },
|
||
],
|
||
},
|
||
{
|
||
poNumber: "PO-2026-004", vendor: vendors[4], orderDate: new Date("2026-02-10"), expectedDate: new Date("2026-03-10"),
|
||
status: "PARTIALLY_RECEIVED", subtotal: 75000, tax: 6750,
|
||
lines: [{ lineNumber: 1, description: "Developer workstations - new hires", quantity: 15, unitPrice: 5000 }],
|
||
},
|
||
{
|
||
poNumber: "PO-2026-005", vendor: vendors[5], orderDate: new Date("2026-03-01"), expectedDate: new Date("2026-03-15"),
|
||
status: "SENT", subtotal: 18000, tax: 1620,
|
||
lines: [{ lineNumber: 1, description: "GCP BigQuery & Vertex AI credits", quantity: 1, unitPrice: 18000 }],
|
||
},
|
||
{
|
||
poNumber: "PO-2026-006", vendor: vendors[6], orderDate: new Date("2026-03-05"), expectedDate: new Date("2026-03-20"),
|
||
status: "APPROVED", subtotal: 9600, tax: 864,
|
||
lines: [{ lineNumber: 1, description: "Datadog Pro - Annual", quantity: 1, unitPrice: 9600 }],
|
||
},
|
||
{
|
||
poNumber: "PO-2026-007", vendor: vendors[3], orderDate: new Date("2026-03-15"), expectedDate: new Date("2026-04-01"),
|
||
status: "SENT", subtotal: 12000, tax: 1080,
|
||
lines: [{ lineNumber: 1, description: "ADP Workforce Now - Q2 processing", quantity: 1, unitPrice: 12000 }],
|
||
},
|
||
{
|
||
poNumber: "PO-2026-008", vendor: vendors[7], orderDate: new Date("2026-01-10"), expectedDate: new Date("2026-01-31"),
|
||
status: "RECEIVED", subtotal: 96000, tax: 8640,
|
||
lines: [{ lineNumber: 1, description: "Annual D&O and E&O insurance premium", quantity: 1, unitPrice: 96000 }],
|
||
},
|
||
];
|
||
|
||
for (const po of poData) {
|
||
const subtotal = po.lines.reduce((sum, line) => sum + (line.quantity * line.unitPrice), 0);
|
||
const totalAmount = subtotal + po.tax;
|
||
const purchaseOrder = await prisma.purchaseOrder.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
poNumber: po.poNumber,
|
||
vendorId: po.vendor.id,
|
||
orderDate: po.orderDate,
|
||
expectedDate: po.expectedDate,
|
||
status: po.status,
|
||
subtotal: subtotal.toString(),
|
||
taxAmount: po.tax.toString(),
|
||
totalAmount: totalAmount.toString(),
|
||
notes: null,
|
||
createdBy: user.id,
|
||
lines: {
|
||
create: po.lines.map(line => ({
|
||
lineNumber: line.lineNumber,
|
||
description: line.description,
|
||
quantity: line.quantity,
|
||
unitPrice: line.unitPrice,
|
||
lineAmount: (line.quantity * line.unitPrice).toString(),
|
||
glAccountCode: null,
|
||
})),
|
||
},
|
||
},
|
||
include: { vendor: true, lines: true },
|
||
});
|
||
purchaseOrders.push(purchaseOrder);
|
||
}
|
||
console.log(`✓ Created ${purchaseOrders.length} purchase orders`);
|
||
|
||
// ========================================
|
||
// CUSTOMERS
|
||
// ========================================
|
||
console.log("👥 Creating customers...");
|
||
const customers = [];
|
||
const customerData = [
|
||
{ code: "C001", name: "ABC Corporation", termDays: 30, type: "Enterprise", email: "ap@abccorp.com", phone: "212-555-1001" },
|
||
{ code: "C002", name: "TechVenture Inc", termDays: 30, type: "SaaS", email: "billing@techventure.io", phone: "415-555-1002" },
|
||
{ code: "C003", name: "Global Partners Ltd", termDays: 45, type: "Consulting", email: "finance@globalpartners.com", phone: "44-20-555-1003" },
|
||
{ code: "C004", name: "Premier Solutions Group", termDays: 30, type: "Enterprise", email: "accounts@premiersolutions.com", phone: "312-555-1004" },
|
||
{ code: "C005", name: "StartUp Ventures", termDays: 15, type: "Startup", email: "cfo@startupventures.co", phone: "650-555-1005" },
|
||
{ code: "C006", name: "Northern Health Systems", termDays: 45, type: "Enterprise", email: "procurement@northernhealth.org", phone: "617-555-1006" },
|
||
{ code: "C007", name: "Pacific Retail Group", termDays: 30, type: "Enterprise", email: "finance@pacificretail.com", phone: "503-555-1007" },
|
||
{ code: "C008", name: "Atlas Financial Corp", termDays: 30, type: "Financial Services", email: "vendor-mgmt@atlasfinancial.com", phone: "704-555-1008" },
|
||
];
|
||
|
||
for (const c of customerData) {
|
||
const customer = await prisma.customer.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
customerCode: c.code,
|
||
customerName: c.name,
|
||
customerType: c.type,
|
||
paymentTermDays: c.termDays,
|
||
email: c.email,
|
||
phone: c.phone,
|
||
status: "ACTIVE",
|
||
},
|
||
});
|
||
customers.push(customer);
|
||
}
|
||
console.log(`✓ Created ${customers.length} customers`);
|
||
|
||
// ========================================
|
||
// AP INVOICES (FY2026 Q1)
|
||
// ========================================
|
||
console.log("📄 Creating AP invoices...");
|
||
const apInvoices = [];
|
||
const apInvoiceData = [
|
||
{ number: "INV-AP-2026-0001", vendor: vendors[0], amount: 22000, status: "PAID" as InvoiceStatus, issueDate: new Date("2026-01-10"), dueDate: new Date("2026-02-15"), paidAmount: 22000,
|
||
lines: [{ accountCode: "6400", description: "AWS Reserved Instances - Jan", amount: 22000 }] },
|
||
{ number: "INV-AP-2026-0002", vendor: vendors[1], amount: 48000, status: "PAID" as InvoiceStatus, issueDate: new Date("2026-01-20"), dueDate: new Date("2026-03-05"), paidAmount: 48000,
|
||
lines: [{ accountCode: "6100", description: "Salesforce Enterprise - Annual", amount: 48000 }] },
|
||
{ number: "INV-AP-2026-0003", vendor: vendors[2], amount: 30000, status: "APPROVED" as InvoiceStatus, issueDate: new Date("2026-02-01"), dueDate: new Date("2026-04-01"),
|
||
lines: [{ accountCode: "6300", description: "Office lease - Q2", amount: 24000 }, { accountCode: "6300", description: "Utilities Q2", amount: 6000 }] },
|
||
{ number: "INV-AP-2026-0004", vendor: vendors[3], amount: 12000, status: "PENDING_APPROVAL" as InvoiceStatus, issueDate: new Date("2026-03-15"), dueDate: new Date("2026-04-15"),
|
||
lines: [{ accountCode: "6300", description: "ADP Payroll Processing Q2", amount: 12000 }] },
|
||
{ number: "INV-AP-2026-0005", vendor: vendors[4], amount: 75000, status: "PARTIALLY_PAID" as InvoiceStatus, issueDate: new Date("2026-02-10"), dueDate: new Date("2026-04-10"), paidAmount: 37500,
|
||
lines: [{ accountCode: "6400", description: "Developer workstations", amount: 75000 }] },
|
||
{ number: "INV-AP-2026-0006", vendor: vendors[0], amount: 24200, status: "APPROVED" as InvoiceStatus, issueDate: new Date("2026-02-28"), dueDate: new Date("2026-03-30"),
|
||
lines: [{ accountCode: "6400", description: "AWS usage - February", amount: 24200 }] },
|
||
{ number: "INV-AP-2026-0007", vendor: vendors[5], amount: 18000, status: "DRAFT" as InvoiceStatus, issueDate: new Date("2026-03-25"), dueDate: new Date("2026-04-30"),
|
||
lines: [{ accountCode: "6400", description: "GCP BigQuery & AI credits", amount: 18000 }] },
|
||
{ number: "INV-AP-2026-0008", vendor: vendors[6], amount: 9600, status: "APPROVED" as InvoiceStatus, issueDate: new Date("2026-03-05"), dueDate: new Date("2026-04-05"),
|
||
lines: [{ accountCode: "6400", description: "Datadog Pro Annual", amount: 9600 }] },
|
||
{ number: "INV-AP-2026-0009", vendor: vendors[7], amount: 96000, status: "PAID" as InvoiceStatus, issueDate: new Date("2026-01-08"), dueDate: new Date("2026-02-10"), paidAmount: 96000,
|
||
lines: [{ accountCode: "1200", description: "Annual D&O + E&O insurance", amount: 96000 }] },
|
||
{ number: "INV-AP-2026-0010", vendor: vendors[2], amount: 8000, status: "OVERDUE" as InvoiceStatus, issueDate: new Date("2026-01-25"), dueDate: new Date("2026-03-01"), daysOverdue: 35,
|
||
lines: [{ accountCode: "6300", description: "Conference room rental Feb", amount: 8000 }] },
|
||
{ number: "INV-AP-2026-0011", vendor: vendors[0], amount: 24200, status: "APPROVED" as InvoiceStatus, issueDate: new Date("2026-03-28"), dueDate: new Date("2026-04-30"),
|
||
lines: [{ accountCode: "6400", description: "AWS usage - March", amount: 24200 }] },
|
||
{ number: "INV-AP-2026-0012", vendor: vendors[4], amount: 15000, status: "DRAFT" as InvoiceStatus, issueDate: new Date("2026-04-01"), dueDate: new Date("2026-05-10"),
|
||
lines: [{ accountCode: "1500", description: "Additional server hardware", amount: 15000 }] },
|
||
];
|
||
|
||
for (const apData of apInvoiceData) {
|
||
const invoiceLines = apData.lines.map((line, index) => ({
|
||
description: line.description, quantity: 1, unitPrice: line.amount, lineAmount: line.amount,
|
||
lineNumber: index + 1, glAccountCode: line.accountCode,
|
||
}));
|
||
const totalAmount = apData.lines.reduce((sum, line) => sum + line.amount, 0);
|
||
const paidAmount = apData.paidAmount ?? (apData.status === "PAID" ? totalAmount : 0);
|
||
const invoice = await prisma.invoice.create({
|
||
data: {
|
||
organizationId: organization.id, entityId: acmeSoftware.id, vendorId: apData.vendor.id,
|
||
invoiceNumber: apData.number, invoiceDate: apData.issueDate, dueDate: apData.dueDate,
|
||
invoiceType: "ACCOUNTS_PAYABLE", status: apData.status,
|
||
subtotalAmount: totalAmount, totalAmount, paidAmount, balanceDue: totalAmount - paidAmount,
|
||
daysOverdue: apData.daysOverdue ?? null,
|
||
invoiceLines: { create: invoiceLines },
|
||
},
|
||
});
|
||
apInvoices.push(invoice);
|
||
}
|
||
console.log(`✓ Created ${apInvoices.length} AP invoices`);
|
||
|
||
// ========================================
|
||
// AR INVOICES (FY2026 Q1)
|
||
// ========================================
|
||
console.log("📄 Creating AR invoices...");
|
||
const arInvoices = [];
|
||
const arInvoiceData = [
|
||
{ number: "INV-AR-2026-0001", customer: customers[0], amount: 185000, status: "PAID" as InvoiceStatus, issueDate: new Date("2026-01-05"), dueDate: new Date("2026-02-14"), paidAmount: 185000,
|
||
lines: [{ accountCode: "4000", description: "Enterprise platform license - Jan", amount: 185000 }] },
|
||
{ number: "INV-AR-2026-0002", customer: customers[1], amount: 45000, status: "PAID" as InvoiceStatus, issueDate: new Date("2026-01-08"), dueDate: new Date("2026-02-14"), paidAmount: 45000,
|
||
lines: [{ accountCode: "4100", description: "SaaS subscription Q1", amount: 45000 }] },
|
||
{ number: "INV-AR-2026-0003", customer: customers[2], amount: 92000, status: "APPROVED" as InvoiceStatus, issueDate: new Date("2026-02-10"), dueDate: new Date("2026-03-30"),
|
||
lines: [{ accountCode: "4100", description: "Consulting engagement - Feb/Mar", amount: 92000 }] },
|
||
{ number: "INV-AR-2026-0004", customer: customers[3], amount: 203500, status: "PAID" as InvoiceStatus, issueDate: new Date("2026-02-01"), dueDate: new Date("2026-03-14"), paidAmount: 203500,
|
||
lines: [{ accountCode: "4000", description: "Enterprise license renewal", amount: 203500 }] },
|
||
{ number: "INV-AR-2026-0005", customer: customers[4], amount: 24000, status: "OVERDUE" as InvoiceStatus, issueDate: new Date("2026-01-15"), dueDate: new Date("2026-02-20"), daysOverdue: 44,
|
||
lines: [{ accountCode: "4100", description: "Startup plan - 12 months", amount: 24000 }] },
|
||
{ number: "INV-AR-2026-0006", customer: customers[0], amount: 101200, status: "PARTIALLY_PAID" as InvoiceStatus, issueDate: new Date("2026-03-01"), dueDate: new Date("2026-04-14"), paidAmount: 50000,
|
||
lines: [{ accountCode: "4000", description: "Implementation services", amount: 101200 }] },
|
||
{ number: "INV-AR-2026-0007", customer: customers[5], amount: 156000, status: "APPROVED" as InvoiceStatus, issueDate: new Date("2026-03-10"), dueDate: new Date("2026-04-30"),
|
||
lines: [{ accountCode: "4000", description: "Healthcare platform license", amount: 156000 }] },
|
||
{ number: "INV-AR-2026-0008", customer: customers[6], amount: 78000, status: "DRAFT" as InvoiceStatus, issueDate: new Date("2026-03-20"), dueDate: new Date("2026-05-15"),
|
||
lines: [{ accountCode: "4000", description: "Retail analytics module", amount: 78000 }] },
|
||
{ number: "INV-AR-2026-0009", customer: customers[7], amount: 210000, status: "APPROVED" as InvoiceStatus, issueDate: new Date("2026-03-15"), dueDate: new Date("2026-04-20"),
|
||
lines: [{ accountCode: "4000", description: "Financial compliance suite", amount: 135000 }, { accountCode: "4100", description: "Integration services", amount: 75000 }] },
|
||
{ number: "INV-AR-2026-0010", customer: customers[1], amount: 15000, status: "APPROVED" as InvoiceStatus, issueDate: new Date("2026-03-28"), dueDate: new Date("2026-04-14"),
|
||
lines: [{ accountCode: "4100", description: "SaaS subscription Q2", amount: 15000 }] },
|
||
];
|
||
|
||
for (const arData of arInvoiceData) {
|
||
const invoiceLines = arData.lines.map((line, index) => ({
|
||
description: line.description, quantity: 1, unitPrice: line.amount, lineAmount: line.amount,
|
||
lineNumber: index + 1, glAccountCode: line.accountCode,
|
||
}));
|
||
const totalAmount = arData.lines.reduce((sum, line) => sum + line.amount, 0);
|
||
const paidAmount = arData.paidAmount ?? (arData.status === "PAID" ? totalAmount : 0);
|
||
const invoice = await prisma.invoice.create({
|
||
data: {
|
||
organizationId: organization.id, entityId: acmeSoftware.id, customerId: arData.customer.id,
|
||
invoiceNumber: arData.number, invoiceDate: arData.issueDate, dueDate: arData.dueDate,
|
||
invoiceType: "ACCOUNTS_RECEIVABLE", status: arData.status,
|
||
subtotalAmount: totalAmount, totalAmount, paidAmount, balanceDue: totalAmount - paidAmount,
|
||
daysOverdue: arData.daysOverdue ?? null,
|
||
invoiceLines: { create: invoiceLines },
|
||
},
|
||
});
|
||
arInvoices.push(invoice);
|
||
}
|
||
console.log(`✓ Created ${arInvoices.length} AR invoices`);
|
||
|
||
// ========================================
|
||
// PAYMENTS
|
||
// ========================================
|
||
console.log("💳 Creating payments...");
|
||
const payments = [];
|
||
|
||
const paymentConfigs = [
|
||
{ vendorId: vendors[0].id, invoiceIdx: 0, number: "PAY-2026-0001", date: "2026-02-10", method: "BANK_TRANSFER" as const, amount: 22000 },
|
||
{ vendorId: vendors[1].id, invoiceIdx: 1, number: "PAY-2026-0002", date: "2026-02-28", method: "BANK_TRANSFER" as const, amount: 48000 },
|
||
{ vendorId: vendors[7].id, invoiceIdx: 8, number: "PAY-2026-0003", date: "2026-02-05", method: "WIRE" as const, amount: 96000 },
|
||
{ vendorId: vendors[4].id, invoiceIdx: 4, number: "PAY-2026-0004", date: "2026-03-20", method: "BANK_TRANSFER" as const, amount: 37500 },
|
||
{ customerId: customers[0].id, invoiceIdx: 0, isAR: true, number: "PAY-2026-0005", date: "2026-02-12", method: "BANK_TRANSFER" as const, amount: 185000 },
|
||
{ customerId: customers[1].id, invoiceIdx: 1, isAR: true, number: "PAY-2026-0006", date: "2026-02-10", method: "ACH" as const, amount: 45000 },
|
||
{ customerId: customers[3].id, invoiceIdx: 3, isAR: true, number: "PAY-2026-0007", date: "2026-03-10", method: "WIRE" as const, amount: 203500 },
|
||
{ customerId: customers[0].id, invoiceIdx: 5, isAR: true, number: "PAY-2026-0008", date: "2026-04-01", method: "CHECK" as const, amount: 50000 },
|
||
];
|
||
|
||
for (const pc of paymentConfigs) {
|
||
const invoiceRef = pc.isAR ? arInvoices[pc.invoiceIdx] : apInvoices[pc.invoiceIdx];
|
||
const payment = await prisma.payment.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
vendorId: pc.vendorId || null,
|
||
customerId: pc.customerId || null,
|
||
invoiceId: invoiceRef.id,
|
||
paymentNumber: pc.number,
|
||
paymentDate: new Date(pc.date),
|
||
paymentMethod: pc.method,
|
||
status: "COMPLETED",
|
||
paymentAmount: pc.amount,
|
||
totalPaymentAmount: pc.amount,
|
||
paymentAllocations: {
|
||
create: [{ organizationId: organization.id, invoiceId: invoiceRef.id, allocatedAmount: pc.amount }],
|
||
},
|
||
},
|
||
});
|
||
payments.push(payment);
|
||
}
|
||
console.log(`✓ Created ${payments.length} payments`);
|
||
|
||
// ========================================
|
||
// BANK ACCOUNTS
|
||
// ========================================
|
||
console.log("🏦 Creating bank accounts...");
|
||
const bankAccounts = [];
|
||
const bankAccountsData = [
|
||
{ name: "Chase Business Checking", bank: "Chase Bank", number: "****2847", type: "Checking", balance: 2845600.75, entity: acmeSoftware },
|
||
{ name: "Bank of America Payroll", bank: "Bank of America", number: "****5923", type: "Checking", balance: 485340.50, entity: acmeSoftware },
|
||
{ name: "Silicon Valley Bank", bank: "Silicon Valley Bank", number: "****7412", type: "Checking", balance: 1524850.00, entity: acmeSoftware },
|
||
{ name: "Mercury Bank", bank: "Mercury", number: "****3156", type: "Savings", balance: 289750.25, entity: acmeSoftware },
|
||
{ name: "Wells Fargo Business Savings", bank: "Wells Fargo", number: "****9284", type: "Savings", balance: 756200.00, entity: acmeSoftware },
|
||
{ name: "Barclays UK Account", bank: "Barclays", number: "****6201", type: "Checking", balance: 342000.00, entity: acmeCloud },
|
||
{ name: "Deutsche Bank EUR", bank: "Deutsche Bank", number: "****8834", type: "Checking", balance: 215000.00, entity: acmeEurope },
|
||
{ name: "ANZ Business", bank: "ANZ", number: "****4477", type: "Checking", balance: 178500.00, entity: acmePacific },
|
||
];
|
||
|
||
for (const accData of bankAccountsData) {
|
||
const account = await prisma.bankAccount.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
entityId: accData.entity.id,
|
||
accountName: accData.name,
|
||
bankName: accData.bank,
|
||
accountNumber: accData.number,
|
||
accountNumberMasked: accData.number,
|
||
accountType: accData.type,
|
||
currencyCode: accData.entity === acmeCloud ? "GBP" : accData.entity === acmeEurope ? "EUR" : accData.entity === acmePacific ? "AUD" : "USD",
|
||
currentBalance: accData.balance,
|
||
availableBalance: accData.balance,
|
||
lastBalanceUpdate: new Date("2026-04-03"),
|
||
status: "ACTIVE",
|
||
},
|
||
});
|
||
bankAccounts.push(account);
|
||
}
|
||
console.log(`✓ Created ${bankAccounts.length} bank accounts`);
|
||
|
||
// ========================================
|
||
// BANK TRANSACTIONS (recent 30 days)
|
||
// ========================================
|
||
console.log("📊 Creating bank transactions...");
|
||
const bankTransactions = [];
|
||
const transactionData = [
|
||
{ account: bankAccounts[0], date: "2026-03-05", type: "CREDIT", amount: 185000, description: "Customer payment - ABC Corp", reconciled: true },
|
||
{ account: bankAccounts[0], date: "2026-03-10", type: "CREDIT", amount: 203500, description: "Customer payment - Premier Solutions", reconciled: true },
|
||
{ account: bankAccounts[0], date: "2026-03-12", type: "DEBIT", amount: 48000, description: "Salesforce annual renewal", reconciled: true },
|
||
{ account: bankAccounts[0], date: "2026-03-15", type: "DEBIT", amount: 22000, description: "AWS cloud hosting", reconciled: true },
|
||
{ account: bankAccounts[0], date: "2026-03-20", type: "DEBIT", amount: 37500, description: "Dell workstations partial", reconciled: true },
|
||
{ account: bankAccounts[0], date: "2026-03-25", type: "CREDIT", amount: 45000, description: "Customer payment - TechVenture", reconciled: true },
|
||
{ account: bankAccounts[0], date: "2026-04-01", type: "CREDIT", amount: 50000, description: "Customer payment - ABC Corp partial", reconciled: false },
|
||
{ account: bankAccounts[0], date: "2026-04-03", type: "DEBIT", amount: 24200, description: "AWS usage - March billing", reconciled: false },
|
||
{ account: bankAccounts[1], date: "2026-03-25", type: "DEBIT", amount: 225000, description: "March payroll - engineering", reconciled: true },
|
||
{ account: bankAccounts[1], date: "2026-03-25", type: "DEBIT", amount: 49500, description: "March payroll - S&M team", reconciled: true },
|
||
{ account: bankAccounts[1], date: "2026-03-25", type: "DEBIT", amount: 41800, description: "March payroll - G&A staff", reconciled: true },
|
||
{ account: bankAccounts[1], date: "2026-03-22", type: "CREDIT", amount: 225000, description: "Payroll account funding - Mar", reconciled: true },
|
||
{ account: bankAccounts[2], date: "2026-03-01", type: "DEBIT", amount: 96000, description: "Insurance premium - annual", reconciled: true },
|
||
{ account: bankAccounts[2], date: "2026-03-15", type: "CREDIT", amount: 92000, description: "Customer payment - Global Partners", reconciled: true },
|
||
{ account: bankAccounts[2], date: "2026-04-02", type: "DEBIT", amount: 105000, description: "Vendor payments batch", reconciled: false },
|
||
{ account: bankAccounts[3], date: "2026-03-31", type: "CREDIT", amount: 2100, description: "Interest earned Q1", reconciled: true },
|
||
{ account: bankAccounts[3], date: "2026-04-01", type: "DEBIT", amount: 150, description: "Monthly maintenance fee", reconciled: true },
|
||
{ account: bankAccounts[4], date: "2026-03-30", type: "DEBIT", amount: 100000, description: "Loan principal payment Q1", reconciled: true },
|
||
{ account: bankAccounts[4], date: "2026-03-30", type: "DEBIT", amount: 3500, description: "Interest payment Q1", reconciled: true },
|
||
{ account: bankAccounts[5], date: "2026-03-15", type: "CREDIT", amount: 142000, description: "UK customer payments Q1", reconciled: true },
|
||
{ account: bankAccounts[5], date: "2026-03-28", type: "DEBIT", amount: 45000, description: "UK operating expenses", reconciled: false },
|
||
{ account: bankAccounts[6], date: "2026-03-16", type: "CREDIT", amount: 108000, description: "Europe customer payments Q1", reconciled: true },
|
||
{ account: bankAccounts[6], date: "2026-03-30", type: "DEBIT", amount: 38000, description: "Europe operating expenses", reconciled: false },
|
||
{ account: bankAccounts[7], date: "2026-03-17", type: "CREDIT", amount: 78000, description: "Pacific customer payments Q1", reconciled: true },
|
||
{ account: bankAccounts[7], date: "2026-03-30", type: "DEBIT", amount: 28000, description: "Pacific operating expenses", reconciled: false },
|
||
];
|
||
|
||
for (const txData of transactionData) {
|
||
const txDate = new Date(txData.date);
|
||
const transaction = await prisma.bankTransaction.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
bankAccountId: txData.account.id,
|
||
transactionDate: txDate,
|
||
postDate: txDate,
|
||
transactionType: txData.type as "DEBIT" | "CREDIT" | "REVERSAL" | "ADJUSTMENT",
|
||
amount: txData.amount,
|
||
description: txData.description,
|
||
reconciled: txData.reconciled,
|
||
reconciledAt: txData.reconciled ? txDate : null,
|
||
status: "PENDING",
|
||
},
|
||
});
|
||
bankTransactions.push(transaction);
|
||
}
|
||
console.log(`✓ Created ${bankTransactions.length} bank transactions`);
|
||
|
||
// ========================================
|
||
// CASH POSITION SNAPSHOTS
|
||
// ========================================
|
||
console.log("💰 Creating cash position snapshots...");
|
||
const cashSnapshots = [
|
||
{ date: "2025-12-31", total: 5250000, byEntity: { "Acme Software Inc": 4450000, "Acme Cloud Services Ltd": 380000, "Acme Europe GmbH": 240000, "Acme Pacific Pty Ltd": 180000 } },
|
||
{ date: "2026-01-31", total: 5680000, byEntity: { "Acme Software Inc": 4820000, "Acme Cloud Services Ltd": 395000, "Acme Europe GmbH": 268000, "Acme Pacific Pty Ltd": 197000 } },
|
||
{ date: "2026-02-28", total: 5920000, byEntity: { "Acme Software Inc": 5010000, "Acme Cloud Services Ltd": 415000, "Acme Europe GmbH": 285000, "Acme Pacific Pty Ltd": 210000 } },
|
||
{ date: "2026-03-31", total: 6137241.50, byEntity: { "Acme Software Inc": 5201741.50, "Acme Cloud Services Ltd": 439000, "Acme Europe GmbH": 310000, "Acme Pacific Pty Ltd": 186500 } },
|
||
{ date: "2026-04-03", total: 6037241.50, byEntity: { "Acme Software Inc": 5101741.50, "Acme Cloud Services Ltd": 439000, "Acme Europe GmbH": 310000, "Acme Pacific Pty Ltd": 186500 } },
|
||
];
|
||
|
||
for (const snap of cashSnapshots) {
|
||
await prisma.cashPosition.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
snapshotDate: new Date(snap.date),
|
||
totalCash: snap.total,
|
||
totalAvailable: snap.total,
|
||
cashByEntity: snap.byEntity,
|
||
cashByCurrency: { USD: snap.byEntity["Acme Software Inc"], GBP: snap.byEntity["Acme Cloud Services Ltd"], EUR: snap.byEntity["Acme Europe GmbH"], AUD: snap.byEntity["Acme Pacific Pty Ltd"] },
|
||
},
|
||
});
|
||
}
|
||
console.log(`✓ Created ${cashSnapshots.length} cash position snapshots`);
|
||
|
||
// ========================================
|
||
// BUDGET LINES (FY2026)
|
||
// ========================================
|
||
console.log("📊 Creating budget lines...");
|
||
const budgetData = [
|
||
{ accountCode: "4000", annual: 2640000, monthly: [200000, 210000, 215000, 220000, 225000, 225000, 220000, 215000, 220000, 230000, 230000, 230000] },
|
||
{ accountCode: "4100", annual: 1200000, monthly: [95000, 98000, 100000, 102000, 100000, 100000, 100000, 102000, 103000, 100000, 100000, 100000] },
|
||
{ accountCode: "4500", annual: 25200, monthly: [2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100] },
|
||
{ accountCode: "5000", annual: 726000, monthly: [60500, 60500, 60500, 60500, 60500, 60500, 60500, 60500, 60500, 60500, 60500, 60500] },
|
||
{ accountCode: "6100", annual: 594000, monthly: [49500, 49500, 49500, 49500, 49500, 49500, 49500, 49500, 49500, 49500, 49500, 49500] },
|
||
{ accountCode: "6200", annual: 2700000, monthly: [225000, 225000, 225000, 225000, 225000, 225000, 225000, 225000, 225000, 225000, 225000, 225000] },
|
||
{ accountCode: "6300", annual: 620400, monthly: [51700, 51700, 51700, 51700, 51700, 51700, 51700, 51700, 51700, 51700, 51700, 51700] },
|
||
{ accountCode: "6400", annual: 290400, monthly: [24200, 24200, 24200, 24200, 24200, 24200, 24200, 24200, 24200, 24200, 24200, 24200] },
|
||
{ accountCode: "7000", annual: 42000, monthly: [3500, 3500, 3500, 3500, 3500, 3500, 3500, 3500, 3500, 3500, 3500, 3500] },
|
||
];
|
||
|
||
for (const bd of budgetData) {
|
||
await prisma.budgetLine.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
accountId: accounts[bd.accountCode].id,
|
||
fiscalYear: 2026,
|
||
monthlyAmounts: bd.monthly,
|
||
annualBudget: bd.annual,
|
||
notes: `FY2026 budget for ${accounts[bd.accountCode].accountName}`,
|
||
},
|
||
});
|
||
}
|
||
console.log(`✓ Created ${budgetData.length} budget lines`);
|
||
|
||
// Also create FY2025 budgets (for YoY comparison)
|
||
const fy2025BudgetData = [
|
||
{ accountCode: "4000", annual: 2400000, monthly: [185000, 190000, 195000, 200000, 205000, 205000, 200000, 200000, 205000, 210000, 200000, 205000] },
|
||
{ accountCode: "4100", annual: 1104000, monthly: [92000, 92000, 92000, 92000, 92000, 92000, 92000, 92000, 92000, 92000, 92000, 92000] },
|
||
{ accountCode: "5000", annual: 660000, monthly: [55000, 55000, 55000, 55000, 55000, 55000, 55000, 55000, 55000, 55000, 55000, 55000] },
|
||
{ accountCode: "6200", annual: 2520000, monthly: [210000, 210000, 210000, 210000, 210000, 210000, 210000, 210000, 210000, 210000, 210000, 210000] },
|
||
{ accountCode: "6100", annual: 540000, monthly: [45000, 45000, 45000, 45000, 45000, 45000, 45000, 45000, 45000, 45000, 45000, 45000] },
|
||
{ accountCode: "6300", annual: 558000, monthly: [46500, 46500, 46500, 46500, 46500, 46500, 46500, 46500, 46500, 46500, 46500, 46500] },
|
||
{ accountCode: "6400", annual: 264000, monthly: [22000, 22000, 22000, 22000, 22000, 22000, 22000, 22000, 22000, 22000, 22000, 22000] },
|
||
];
|
||
|
||
for (const bd of fy2025BudgetData) {
|
||
await prisma.budgetLine.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
accountId: accounts[bd.accountCode].id,
|
||
fiscalYear: 2025,
|
||
monthlyAmounts: bd.monthly,
|
||
annualBudget: bd.annual,
|
||
notes: `FY2025 budget for ${accounts[bd.accountCode].accountName}`,
|
||
},
|
||
});
|
||
}
|
||
console.log(`✓ Created ${fy2025BudgetData.length} FY2025 budget lines`);
|
||
|
||
// ========================================
|
||
// LEASES (ASC 842)
|
||
// ========================================
|
||
console.log("🏢 Creating leases...");
|
||
const leases = [
|
||
{
|
||
description: "NYC Office Space - 5th Avenue",
|
||
type: "Operating",
|
||
startDate: new Date("2024-01-01"),
|
||
endDate: new Date("2028-12-31"),
|
||
monthlyPayment: 24000,
|
||
totalCommitment: 1440000,
|
||
discountRate: 4.5,
|
||
rouAsset: 1296000,
|
||
leaseLiability: 1310400,
|
||
currentPortion: 276480,
|
||
nonCurrentPortion: 1033920,
|
||
status: "Active",
|
||
},
|
||
{
|
||
description: "London Office - Canary Wharf",
|
||
type: "Operating",
|
||
startDate: new Date("2024-06-01"),
|
||
endDate: new Date("2027-05-31"),
|
||
monthlyPayment: 15000,
|
||
totalCommitment: 540000,
|
||
discountRate: 5.0,
|
||
rouAsset: 486000,
|
||
leaseLiability: 495000,
|
||
currentPortion: 175500,
|
||
nonCurrentPortion: 319500,
|
||
status: "Active",
|
||
},
|
||
{
|
||
description: "Server Co-location - Equinix NY5",
|
||
type: "Finance",
|
||
startDate: new Date("2025-03-01"),
|
||
endDate: new Date("2030-02-28"),
|
||
monthlyPayment: 35000,
|
||
totalCommitment: 2100000,
|
||
discountRate: 3.8,
|
||
rouAsset: 1890000,
|
||
leaseLiability: 1932000,
|
||
currentPortion: 396000,
|
||
nonCurrentPortion: 1536000,
|
||
status: "Active",
|
||
},
|
||
{
|
||
description: "Company Vehicles Fleet (12 units)",
|
||
type: "Operating",
|
||
startDate: new Date("2025-01-01"),
|
||
endDate: new Date("2027-12-31"),
|
||
monthlyPayment: 6000,
|
||
totalCommitment: 216000,
|
||
discountRate: 4.0,
|
||
rouAsset: 194400,
|
||
leaseLiability: 198000,
|
||
currentPortion: 70200,
|
||
nonCurrentPortion: 127800,
|
||
status: "Active",
|
||
},
|
||
{
|
||
description: "Berlin Office - Alexanderplatz",
|
||
type: "Operating",
|
||
startDate: new Date("2025-06-01"),
|
||
endDate: new Date("2028-05-31"),
|
||
monthlyPayment: 8500,
|
||
totalCommitment: 306000,
|
||
discountRate: 4.2,
|
||
rouAsset: 275400,
|
||
leaseLiability: 280800,
|
||
currentPortion: 99000,
|
||
nonCurrentPortion: 181800,
|
||
status: "Active",
|
||
},
|
||
{
|
||
description: "Old SF Office (terminated early)",
|
||
type: "Operating",
|
||
startDate: new Date("2022-01-01"),
|
||
endDate: new Date("2025-06-30"),
|
||
monthlyPayment: 18000,
|
||
totalCommitment: 756000,
|
||
discountRate: 3.5,
|
||
rouAsset: 0,
|
||
leaseLiability: 0,
|
||
currentPortion: 0,
|
||
nonCurrentPortion: 0,
|
||
status: "Expired",
|
||
},
|
||
];
|
||
|
||
for (const l of leases) {
|
||
await prisma.lease.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
description: l.description,
|
||
type: l.type,
|
||
startDate: l.startDate,
|
||
endDate: l.endDate,
|
||
monthlyPayment: l.monthlyPayment,
|
||
totalCommitment: l.totalCommitment,
|
||
discountRate: l.discountRate,
|
||
rouAsset: l.rouAsset,
|
||
leaseLiability: l.leaseLiability,
|
||
currentPortion: l.currentPortion,
|
||
nonCurrentPortion: l.nonCurrentPortion,
|
||
status: l.status,
|
||
},
|
||
});
|
||
}
|
||
console.log(`✓ Created ${leases.length} leases`);
|
||
|
||
// ========================================
|
||
// SHAREHOLDERS / CAP TABLE
|
||
// ========================================
|
||
console.log("📋 Creating shareholders...");
|
||
const shareholders = [
|
||
{ name: "Ryan Francis", type: "founder", sharesOwned: 4000000, shareClass: "Common", vestingCliffMonths: 12, vestingTotalMonths: 48, vestedPercent: 87.50, grantDate: new Date("2020-01-01"), exercisePrice: 0.01, status: "ACTIVE" },
|
||
{ name: "Sarah Chen", type: "founder", sharesOwned: 3000000, shareClass: "Common", vestingCliffMonths: 12, vestingTotalMonths: 48, vestedPercent: 87.50, grantDate: new Date("2020-01-01"), exercisePrice: 0.01, status: "ACTIVE" },
|
||
{ name: "Velocity Partners Fund III", type: "investor", sharesOwned: 2500000, shareClass: "Series A", vestingCliffMonths: 0, vestingTotalMonths: 0, vestedPercent: 100.00, grantDate: new Date("2022-06-15"), exercisePrice: 1.20, status: "ACTIVE" },
|
||
{ name: "Summit Capital Growth", type: "investor", sharesOwned: 3500000, shareClass: "Series B", vestingCliffMonths: 0, vestingTotalMonths: 0, vestedPercent: 100.00, grantDate: new Date("2025-01-15"), exercisePrice: 2.85, status: "ACTIVE" },
|
||
{ name: "Emily Rodriguez", type: "employee", sharesOwned: 500000, shareClass: "Options", vestingCliffMonths: 12, vestingTotalMonths: 48, vestedPercent: 62.50, grantDate: new Date("2021-06-01"), exercisePrice: 0.50, status: "ACTIVE" },
|
||
{ name: "James Park", type: "employee", sharesOwned: 400000, shareClass: "Options", vestingCliffMonths: 12, vestingTotalMonths: 48, vestedPercent: 50.00, grantDate: new Date("2022-01-15"), exercisePrice: 0.80, status: "ACTIVE" },
|
||
{ name: "Aisha Patel", type: "employee", sharesOwned: 350000, shareClass: "Options", vestingCliffMonths: 12, vestingTotalMonths: 48, vestedPercent: 37.50, grantDate: new Date("2023-01-01"), exercisePrice: 1.50, status: "ACTIVE" },
|
||
{ name: "David Kim", type: "advisor", sharesOwned: 150000, shareClass: "Options", vestingCliffMonths: 6, vestingTotalMonths: 24, vestedPercent: 100.00, grantDate: new Date("2022-01-01"), exercisePrice: 0.80, status: "ACTIVE" },
|
||
{ name: "Maria Santos", type: "employee", sharesOwned: 250000, shareClass: "Options", vestingCliffMonths: 12, vestingTotalMonths: 48, vestedPercent: 18.75, grantDate: new Date("2024-04-01"), exercisePrice: 2.00, status: "ACTIVE" },
|
||
{ name: "Alex Thompson (departed)", type: "employee", sharesOwned: 200000, shareClass: "Options", vestingCliffMonths: 12, vestingTotalMonths: 48, vestedPercent: 25.00, grantDate: new Date("2023-01-01"), exercisePrice: 1.50, status: "TERMINATED" },
|
||
];
|
||
|
||
for (const sh of shareholders) {
|
||
await prisma.shareholder.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
name: sh.name,
|
||
type: sh.type,
|
||
sharesOwned: sh.sharesOwned,
|
||
shareClass: sh.shareClass,
|
||
vestingCliffMonths: sh.vestingCliffMonths,
|
||
vestingTotalMonths: sh.vestingTotalMonths,
|
||
vestedPercent: sh.vestedPercent,
|
||
grantDate: sh.grantDate,
|
||
exercisePrice: sh.exercisePrice,
|
||
status: sh.status,
|
||
},
|
||
});
|
||
}
|
||
console.log(`✓ Created ${shareholders.length} shareholders`);
|
||
|
||
// ========================================
|
||
// SUBSCRIPTIONS (customer recurring revenue)
|
||
// ========================================
|
||
console.log("🔄 Creating subscriptions...");
|
||
const subscriptions = [
|
||
{ customer: customers[0], plan: "Enterprise Platform", cycle: "ANNUAL", amount: 2220000, start: "2025-01-01", next: "2026-01-01", status: "ACTIVE" },
|
||
{ customer: customers[1], plan: "SaaS Professional", cycle: "MONTHLY", amount: 15000, start: "2025-03-01", next: "2026-05-01", status: "ACTIVE" },
|
||
{ customer: customers[2], plan: "Consulting Retainer", cycle: "QUARTERLY", amount: 92000, start: "2025-01-01", next: "2026-07-01", status: "ACTIVE" },
|
||
{ customer: customers[3], plan: "Enterprise License", cycle: "ANNUAL", amount: 2442000, start: "2026-01-01", next: "2027-01-01", status: "ACTIVE" },
|
||
{ customer: customers[4], plan: "Startup Plan", cycle: "ANNUAL", amount: 24000, start: "2025-06-01", next: "2026-06-01", status: "ACTIVE" },
|
||
{ customer: customers[5], plan: "Healthcare Suite", cycle: "ANNUAL", amount: 1872000, start: "2026-01-01", next: "2027-01-01", status: "ACTIVE" },
|
||
{ customer: customers[6], plan: "Retail Analytics", cycle: "MONTHLY", amount: 6500, start: "2025-09-01", next: "2026-05-01", status: "ACTIVE" },
|
||
{ customer: customers[7], plan: "Financial Compliance", cycle: "ANNUAL", amount: 2520000, start: "2026-02-01", next: "2027-02-01", status: "ACTIVE" },
|
||
{ customer: customers[1], plan: "SaaS Basic (old)", cycle: "MONTHLY", amount: 8000, start: "2024-01-01", end: "2025-02-28", status: "CANCELLED" },
|
||
];
|
||
|
||
for (const sub of subscriptions) {
|
||
await prisma.subscription.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
customerId: sub.customer.id,
|
||
planName: sub.plan,
|
||
billingCycle: sub.cycle,
|
||
amount: sub.amount,
|
||
startDate: new Date(sub.start),
|
||
endDate: sub.end ? new Date(sub.end) : null,
|
||
nextBillingDate: sub.next ? new Date(sub.next) : null,
|
||
cancelledAt: sub.status === "CANCELLED" ? new Date(sub.end!) : null,
|
||
status: sub.status,
|
||
},
|
||
});
|
||
}
|
||
console.log(`✓ Created ${subscriptions.length} subscriptions`);
|
||
|
||
// ========================================
|
||
// FORECASTS
|
||
// ========================================
|
||
console.log("🔮 Creating forecasts...");
|
||
const forecasts = [
|
||
{
|
||
name: "FY2026 Base Case Forecast",
|
||
description: "Conservative growth scenario based on current pipeline and retention rates",
|
||
scenarioType: "BASE",
|
||
startDate: new Date("2026-01-01"),
|
||
endDate: new Date("2026-12-31"),
|
||
status: "ACTIVE",
|
||
assumptions: { revenueGrowth: 0.10, expenseGrowth: 0.08, churnRate: 0.05, newCustomers: 12 },
|
||
projections: {
|
||
months: months.map((m, i) => ({
|
||
month: `${m} 2026`,
|
||
revenue: 304700 + (i * 3000),
|
||
expenses: 350400 + (i * 1500),
|
||
netIncome: -45700 + (i * 1500),
|
||
cashBalance: 6037241 + (i * 45000),
|
||
})),
|
||
},
|
||
},
|
||
{
|
||
name: "FY2026 Optimistic Scenario",
|
||
description: "Aggressive growth with accelerated enterprise sales and expansion revenue",
|
||
scenarioType: "OPTIMISTIC",
|
||
startDate: new Date("2026-01-01"),
|
||
endDate: new Date("2026-12-31"),
|
||
status: "ACTIVE",
|
||
assumptions: { revenueGrowth: 0.25, expenseGrowth: 0.15, churnRate: 0.03, newCustomers: 24 },
|
||
projections: {
|
||
months: months.map((m, i) => ({
|
||
month: `${m} 2026`,
|
||
revenue: 345000 + (i * 8000),
|
||
expenses: 365000 + (i * 3000),
|
||
netIncome: -20000 + (i * 5000),
|
||
cashBalance: 6037241 + (i * 85000),
|
||
})),
|
||
},
|
||
},
|
||
{
|
||
name: "FY2026 Pessimistic Scenario",
|
||
description: "Downturn scenario with reduced pipeline and higher churn",
|
||
scenarioType: "PESSIMISTIC",
|
||
startDate: new Date("2026-01-01"),
|
||
endDate: new Date("2026-12-31"),
|
||
status: "ACTIVE",
|
||
assumptions: { revenueGrowth: -0.05, expenseGrowth: 0.03, churnRate: 0.12, newCustomers: 4 },
|
||
projections: {
|
||
months: months.map((m, i) => ({
|
||
month: `${m} 2026`,
|
||
revenue: 270000 - (i * 2000),
|
||
expenses: 345000 + (i * 500),
|
||
netIncome: -75000 - (i * 2500),
|
||
cashBalance: 6037241 - (i * 35000),
|
||
})),
|
||
},
|
||
},
|
||
];
|
||
|
||
for (const f of forecasts) {
|
||
await prisma.forecast.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
name: f.name,
|
||
description: f.description,
|
||
scenarioType: f.scenarioType,
|
||
startDate: f.startDate,
|
||
endDate: f.endDate,
|
||
status: f.status,
|
||
assumptions: f.assumptions,
|
||
projections: f.projections,
|
||
createdBy: user.name,
|
||
},
|
||
});
|
||
}
|
||
console.log(`✓ Created ${forecasts.length} forecasts`);
|
||
|
||
// ========================================
|
||
// DOCUMENTS
|
||
// ========================================
|
||
console.log("📁 Creating documents...");
|
||
const documents = [
|
||
{ name: "FY2025 Annual Report.pdf", category: "report", mimeType: "application/pdf", fileSize: 2450000, storagePath: "/documents/reports/fy2025-annual.pdf", tags: ["annual", "FY2025", "financial"] },
|
||
{ name: "Series B Term Sheet.pdf", category: "legal", mimeType: "application/pdf", fileSize: 185000, storagePath: "/documents/legal/series-b-term-sheet.pdf", tags: ["legal", "fundraising", "Series B"] },
|
||
{ name: "AWS MSA.pdf", category: "contract", mimeType: "application/pdf", fileSize: 420000, storagePath: "/documents/contracts/aws-msa.pdf", tags: ["contract", "vendor", "AWS"] },
|
||
{ name: "Q1 2026 Board Deck.pptx", category: "report", mimeType: "application/vnd.openxmlformats-officedocument.presentationml.presentation", fileSize: 5200000, storagePath: "/documents/reports/q1-2026-board-deck.pptx", tags: ["board", "Q1", "2026"] },
|
||
{ name: "Office Lease Agreement - NYC.pdf", category: "contract", mimeType: "application/pdf", fileSize: 890000, storagePath: "/documents/contracts/nyc-office-lease.pdf", tags: ["lease", "real estate", "NYC"] },
|
||
{ name: "D&O Insurance Policy.pdf", category: "legal", mimeType: "application/pdf", fileSize: 340000, storagePath: "/documents/legal/do-insurance-2026.pdf", tags: ["insurance", "D&O", "2026"] },
|
||
{ name: "Employee Stock Option Plan.pdf", category: "legal", mimeType: "application/pdf", fileSize: 275000, storagePath: "/documents/legal/esop-plan.pdf", tags: ["ESOP", "equity", "options"] },
|
||
{ name: "SOC 2 Type II Report.pdf", category: "report", mimeType: "application/pdf", fileSize: 1800000, storagePath: "/documents/compliance/soc2-type2-2025.pdf", tags: ["compliance", "SOC2", "audit"] },
|
||
{ name: "Revenue Recognition Policy.docx", category: "report", mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", fileSize: 125000, storagePath: "/documents/policies/rev-rec-policy.docx", tags: ["policy", "ASC 606", "revenue"] },
|
||
{ name: "ABC Corp Master Agreement.pdf", category: "contract", mimeType: "application/pdf", fileSize: 560000, storagePath: "/documents/contracts/abc-corp-msa.pdf", tags: ["contract", "customer", "ABC Corp"] },
|
||
];
|
||
|
||
for (const doc of documents) {
|
||
await prisma.document.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
name: doc.name,
|
||
category: doc.category,
|
||
mimeType: doc.mimeType,
|
||
fileSize: doc.fileSize,
|
||
storagePath: doc.storagePath,
|
||
status: "ACTIVE",
|
||
tags: doc.tags,
|
||
uploadedBy: user.name,
|
||
},
|
||
});
|
||
}
|
||
console.log(`✓ Created ${documents.length} documents`);
|
||
|
||
// ========================================
|
||
// AUDIT LOG
|
||
// ========================================
|
||
console.log("📜 Creating audit log entries...");
|
||
const auditEntries = [
|
||
{ action: "LOGIN", module: "auth", entityType: "User", description: "User logged in", date: "2026-04-05T08:30:00Z" },
|
||
{ action: "CREATE", module: "general-ledger", entityType: "JournalEntry", description: "Created JE-2026-0001 - January software revenue", date: "2026-01-06T09:15:00Z" },
|
||
{ action: "POST", module: "general-ledger", entityType: "JournalEntry", description: "Posted JE-2026-0001", date: "2026-01-06T09:20:00Z" },
|
||
{ action: "APPROVE", module: "ap", entityType: "Invoice", description: "Approved INV-AP-2026-0003 - WeWork office lease", date: "2026-02-15T14:00:00Z" },
|
||
{ action: "CREATE", module: "ap", entityType: "Payment", description: "Created payment PAY-2026-0001 for AWS", date: "2026-02-10T10:30:00Z" },
|
||
{ action: "APPROVE", module: "ar", entityType: "Invoice", description: "Approved INV-AR-2026-0007 - Northern Health Systems", date: "2026-03-20T11:00:00Z" },
|
||
{ action: "EXPORT", module: "reports", entityType: "Report", description: "Exported Q1 2026 Trial Balance to CSV", date: "2026-04-02T16:45:00Z" },
|
||
{ action: "UPDATE", module: "treasury", entityType: "BankAccount", description: "Updated Chase Business Checking balance", date: "2026-04-03T09:00:00Z" },
|
||
{ action: "CREATE", module: "general-ledger", entityType: "JournalEntry", description: "Created April 2026 revenue accrual (draft)", date: "2026-04-03T10:00:00Z" },
|
||
{ action: "CREATE", module: "ar", entityType: "Invoice", description: "Created INV-AR-2026-0008 - Pacific Retail Group", date: "2026-04-01T13:30:00Z" },
|
||
{ action: "LOGIN", module: "auth", entityType: "User", description: "User logged in from new device", date: "2026-04-04T07:45:00Z" },
|
||
{ action: "UPDATE", module: "planning", entityType: "BudgetLine", description: "Updated FY2026 engineering budget", date: "2026-03-15T15:20:00Z" },
|
||
];
|
||
|
||
for (const entry of auditEntries) {
|
||
await prisma.auditLog.create({
|
||
data: {
|
||
organizationId: organization.id,
|
||
userId: user.id,
|
||
userName: user.name,
|
||
action: entry.action,
|
||
module: entry.module,
|
||
entityType: entry.entityType,
|
||
description: entry.description,
|
||
createdAt: new Date(entry.date),
|
||
},
|
||
});
|
||
}
|
||
console.log(`✓ Created ${auditEntries.length} audit log entries`);
|
||
|
||
// ========================================
|
||
// SUMMARY
|
||
// ========================================
|
||
console.log("\n✅ Database seed completed successfully!");
|
||
console.log(`
|
||
Organization: ${organization.name}
|
||
Entities: ${entities.length}
|
||
Users: 1 (${user.email})
|
||
Accounts: ${accountsData.length}
|
||
Fiscal Periods: 36 (FY2024-FY2026)
|
||
Journal Entries: ${jeCounter} (spanning Jan 2025 - Apr 2026)
|
||
Vendors: ${vendors.length}
|
||
Purchase Orders: ${purchaseOrders.length}
|
||
Customers: ${customers.length}
|
||
AP Invoices: ${apInvoices.length}
|
||
AR Invoices: ${arInvoices.length}
|
||
Payments: ${payments.length}
|
||
Bank Accounts: ${bankAccounts.length}
|
||
Bank Transactions: ${bankTransactions.length}
|
||
Cash Position Snapshots: ${cashSnapshots.length}
|
||
Budget Lines: ${budgetData.length + fy2025BudgetData.length} (FY2025 + FY2026)
|
||
Leases: ${leases.length}
|
||
Shareholders: ${shareholders.length}
|
||
Subscriptions: ${subscriptions.length}
|
||
Forecasts: ${forecasts.length}
|
||
Documents: ${documents.length}
|
||
Audit Log Entries: ${auditEntries.length}
|
||
`);
|
||
}
|
||
|
||
main()
|
||
.then(async () => {
|
||
await prisma.$disconnect();
|
||
})
|
||
.catch(async (e) => {
|
||
console.error("❌ Seed error:", e);
|
||
await prisma.$disconnect();
|
||
process.exit(1);
|
||
});
|