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 } }); } }