Productionize EventSphere platform
This commit is contained in:
141
apps/api/src/modules/crm/crm.service.ts
Normal file
141
apps/api/src/modules/crm/crm.service.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||
import { PrismaService } from "../../prisma/prisma.service";
|
||||
|
||||
@Injectable()
|
||||
export class CrmService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
async summary(tenantId: string) {
|
||||
const [leadCount, dealCount, wonDeals, openDeals, leadsByStatus] = await Promise.all([
|
||||
this.prisma.cRMLead.count({ where: { tenantId } }),
|
||||
this.prisma.cRMDeal.count({ where: { tenantId } }),
|
||||
this.prisma.cRMDeal.aggregate({ where: { tenantId, stage: "closed_won" }, _sum: { valueKobo: true } }),
|
||||
this.prisma.cRMDeal.aggregate({ where: { tenantId, stage: { notIn: ["closed_won", "closed_lost"] } }, _sum: { valueKobo: true } }),
|
||||
this.prisma.cRMLead.groupBy({ by: ["status"], where: { tenantId }, _count: { _all: true } })
|
||||
]);
|
||||
|
||||
return {
|
||||
leadCount,
|
||||
dealCount,
|
||||
wonValueKobo: wonDeals._sum.valueKobo ?? 0,
|
||||
openValueKobo: openDeals._sum.valueKobo ?? 0,
|
||||
leadsByStatus: leadsByStatus.map((row) => ({ status: row.status, count: row._count._all }))
|
||||
};
|
||||
}
|
||||
|
||||
leads(tenantId: string) {
|
||||
return this.prisma.cRMLead.findMany({
|
||||
where: { tenantId },
|
||||
orderBy: { createdAt: "desc" },
|
||||
include: { event: true, attendee: true, deals: true }
|
||||
});
|
||||
}
|
||||
|
||||
createLead(tenantId: string, payload: any) {
|
||||
return this.prisma.cRMLead.create({
|
||||
data: {
|
||||
tenantId,
|
||||
eventId: payload.eventId || null,
|
||||
attendeeId: payload.attendeeId || null,
|
||||
fullName: String(payload.fullName ?? ""),
|
||||
email: String(payload.email ?? "").toLowerCase().trim(),
|
||||
phone: payload.phone ? String(payload.phone) : null,
|
||||
source: payload.source ? String(payload.source) : "manual",
|
||||
status: payload.status ? String(payload.status) : "new",
|
||||
valueKobo: Number(payload.valueKobo ?? 0)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async updateLead(tenantId: string, id: string, payload: any) {
|
||||
const row = await this.prisma.cRMLead.findFirst({ where: { tenantId, id } });
|
||||
if (!row) throw new NotFoundException("Lead not found");
|
||||
return this.prisma.cRMLead.update({
|
||||
where: { id },
|
||||
data: {
|
||||
fullName: payload.fullName === undefined ? undefined : String(payload.fullName),
|
||||
email: payload.email === undefined ? undefined : String(payload.email).toLowerCase().trim(),
|
||||
phone: payload.phone === undefined ? undefined : payload.phone ? String(payload.phone) : null,
|
||||
source: payload.source === undefined ? undefined : String(payload.source),
|
||||
status: payload.status === undefined ? undefined : String(payload.status),
|
||||
valueKobo: payload.valueKobo === undefined ? undefined : Number(payload.valueKobo)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async removeLead(tenantId: string, id: string) {
|
||||
const row = await this.prisma.cRMLead.findFirst({ where: { tenantId, id } });
|
||||
if (!row) throw new NotFoundException("Lead not found");
|
||||
await this.prisma.cRMLead.delete({ where: { id } });
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
deals(tenantId: string) {
|
||||
return this.prisma.cRMDeal.findMany({
|
||||
where: { tenantId },
|
||||
orderBy: { updatedAt: "desc" },
|
||||
include: { lead: true, event: true, activities: true }
|
||||
});
|
||||
}
|
||||
|
||||
createDeal(tenantId: string, payload: any) {
|
||||
return this.prisma.cRMDeal.create({
|
||||
data: {
|
||||
tenantId,
|
||||
leadId: payload.leadId || null,
|
||||
eventId: payload.eventId || null,
|
||||
title: String(payload.title ?? ""),
|
||||
stage: payload.stage ? String(payload.stage) : "new",
|
||||
valueKobo: Number(payload.valueKobo ?? 0),
|
||||
probability: Number(payload.probability ?? 0),
|
||||
ownerUserId: payload.ownerUserId || null,
|
||||
expectedCloseAt: payload.expectedCloseAt ? new Date(payload.expectedCloseAt) : null
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async updateDeal(tenantId: string, id: string, payload: any) {
|
||||
const row = await this.prisma.cRMDeal.findFirst({ where: { tenantId, id } });
|
||||
if (!row) throw new NotFoundException("Deal not found");
|
||||
return this.prisma.cRMDeal.update({
|
||||
where: { id },
|
||||
data: {
|
||||
title: payload.title === undefined ? undefined : String(payload.title),
|
||||
stage: payload.stage === undefined ? undefined : String(payload.stage),
|
||||
valueKobo: payload.valueKobo === undefined ? undefined : Number(payload.valueKobo),
|
||||
probability: payload.probability === undefined ? undefined : Number(payload.probability),
|
||||
expectedCloseAt: payload.expectedCloseAt === undefined ? undefined : payload.expectedCloseAt ? new Date(payload.expectedCloseAt) : null
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async removeDeal(tenantId: string, id: string) {
|
||||
const row = await this.prisma.cRMDeal.findFirst({ where: { tenantId, id } });
|
||||
if (!row) throw new NotFoundException("Deal not found");
|
||||
await this.prisma.cRMDeal.delete({ where: { id } });
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
activities(tenantId: string) {
|
||||
return this.prisma.cRMActivity.findMany({
|
||||
where: { tenantId },
|
||||
orderBy: { createdAt: "desc" },
|
||||
include: { lead: true, deal: true }
|
||||
});
|
||||
}
|
||||
|
||||
createActivity(tenantId: string, userId: string, payload: any) {
|
||||
return this.prisma.cRMActivity.create({
|
||||
data: {
|
||||
tenantId,
|
||||
userId,
|
||||
leadId: payload.leadId || null,
|
||||
dealId: payload.dealId || null,
|
||||
type: payload.type ? String(payload.type) : "note",
|
||||
note: String(payload.note ?? ""),
|
||||
dueAt: payload.dueAt ? new Date(payload.dueAt) : null,
|
||||
completedAt: payload.completedAt ? new Date(payload.completedAt) : null
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user