102 lines
3.7 KiB
TypeScript
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());
|