Files
Andres Resiri 70daeb214a
CI / Quality gate (push) Has been cancelled
Initial project commit for Coolify staging
2026-08-03 10:45:41 -04:00

102 lines
3.7 KiB
TypeScript

import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
async function main() {
console.log("Seeding CRM deals + revenue attribution...");
const brands = await prisma.brand.findMany();
if (brands.length === 0) { console.log("No brands. Run main seed first."); return; }
for (const brand of brands) {
// Clear existing
await prisma.revenueAttribution.deleteMany({ where: { brandId: brand.id } });
await prisma.crmDeal.deleteMany({ where: { brandId: brand.id } });
// Get some conversions to link
const conversions = await prisma.conversionEvent.findMany({
where: { brandId: brand.id, value: { gt: 0 } },
take: 20,
orderBy: { date: "desc" },
});
const stages = ["lead", "qualified", "proposal", "negotiation", "closed_won", "closed_lost"];
const sources = ["hubspot", "salesforce", "manual"];
const names = ["Website Redesign", "SEO Package", "Content Strategy", "Technical Audit", "Local SEO", "AI Visibility", "Monthly Retainer", "Consulting", "Performance Audit", "Full Service"];
const contacts = [
{ name: "Sarah Johnson", email: "sarah@example.com" },
{ name: "Mike Chen", email: "mike@company.co" },
{ name: "Emily Davis", email: "emily@startup.io" },
{ name: "James Wilson", email: "james@agency.com" },
{ name: "Lisa Park", email: "lisa@enterprise.com" },
];
const deals: Array<{ id: string; value: number; channel: string; page: string; type: string; date: Date }> = [];
for (let i = 0; i < 15; i++) {
const daysAgo = Math.floor(Math.random() * 60);
const date = new Date();
date.setDate(date.getDate() - daysAgo);
const stage = stages[Math.floor(Math.random() * stages.length)];
const status = stage === "closed_won" ? "won" : stage === "closed_lost" ? "lost" : "open";
const value = Math.round((500 + Math.random() * 9500) / 100) * 100;
const contact = contacts[i % contacts.length];
const conv = conversions[i % conversions.length] || null;
const deal = await prisma.crmDeal.create({
data: {
brandId: brand.id,
externalId: `ext_${brand.id}_${i}`,
source: sources[Math.floor(Math.random() * sources.length)],
contactName: contact.name,
contactEmail: contact.email,
dealName: `${names[i % names.length]}${contact.name}`,
value,
stage,
status,
closeDate: status !== "open" ? date : null,
conversionId: conv?.id || null,
createdAt: date,
},
});
deals.push({
id: deal.id,
value,
channel: conv?.channel || ["organic", "direct", "social"][Math.floor(Math.random() * 3)],
page: conv?.landingPage || ["/", "/pricing", "/features", "/contact"][Math.floor(Math.random() * 4)],
type: conv?.type || ["form", "phone", "order"][Math.floor(Math.random() * 3)],
date,
});
}
console.log(` CRM: 15 deals for ${brand.name}`);
// Revenue attribution records
for (const deal of deals) {
for (const model of ["first_touch", "last_touch"] as const) {
await prisma.revenueAttribution.create({
data: {
brandId: brand.id,
attributionModel: model,
channel: deal.channel,
landingPage: deal.page,
conversionType: deal.type,
revenue: deal.value,
creditPct: 100,
date: deal.date,
dealId: deal.id,
},
});
}
}
console.log(` Attribution: ${deals.length * 2} records (first + last touch) for ${brand.name}`);
}
console.log("CRM + attribution seeding complete!");
}
main()
.catch((e) => { console.error(e); process.exit(1); })
.finally(() => prisma.$disconnect());