This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
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());
|
||||
Reference in New Issue
Block a user