158 lines
4.4 KiB
TypeScript
158 lines
4.4 KiB
TypeScript
import {
|
|
Injectable,
|
|
NotFoundException,
|
|
BadRequestException,
|
|
} from '@nestjs/common';
|
|
import { PrismaService } from '../prisma/prisma.service';
|
|
import { CreateTicketDto } from './dto/create-ticket.dto';
|
|
import { UpdateTicketDto } from './dto/update-ticket.dto';
|
|
|
|
export interface TicketResolvedEvent {
|
|
ticketId: string;
|
|
tenantId: string;
|
|
clientId: string | null;
|
|
type: string;
|
|
}
|
|
|
|
@Injectable()
|
|
export class TicketService {
|
|
// Event handler for ticket resolution — set by SubscriptionService
|
|
onTicketResolved: ((event: TicketResolvedEvent) => Promise<void>) | null =
|
|
null;
|
|
|
|
constructor(private readonly prisma: PrismaService) {}
|
|
|
|
async findAll(tenantId: string, filters?: { clientId?: string; status?: string; type?: string }) {
|
|
const db = this.prisma.forTenant(tenantId);
|
|
return db.ticket.findMany({
|
|
where: {
|
|
...(filters?.clientId && { clientId: filters.clientId }),
|
|
...(filters?.status && { status: filters.status }),
|
|
...(filters?.type && { type: filters.type }),
|
|
},
|
|
include: {
|
|
client: { select: { id: true, firstName: true, lastName: true, accountNumber: true } },
|
|
assignee: { select: { id: true, firstName: true, lastName: true } },
|
|
createdBy: { select: { id: true, firstName: true, lastName: true } },
|
|
},
|
|
orderBy: { createdAt: 'desc' },
|
|
});
|
|
}
|
|
|
|
async findById(tenantId: string, id: string) {
|
|
const db = this.prisma.forTenant(tenantId);
|
|
const ticket = await db.ticket.findFirst({
|
|
where: { id },
|
|
include: {
|
|
client: true,
|
|
assignee: { select: { id: true, firstName: true, lastName: true } },
|
|
createdBy: { select: { id: true, firstName: true, lastName: true } },
|
|
},
|
|
});
|
|
|
|
if (!ticket) {
|
|
throw new NotFoundException('Ticket not found');
|
|
}
|
|
|
|
return ticket;
|
|
}
|
|
|
|
async create(tenantId: string, createdById: string, dto: CreateTicketDto) {
|
|
return this.prisma.ticket.create({
|
|
data: {
|
|
tenantId,
|
|
clientId: dto.clientId,
|
|
createdById,
|
|
assigneeId: dto.assigneeId,
|
|
type: dto.type,
|
|
title: dto.title,
|
|
description: dto.description,
|
|
priority: dto.priority ?? 'normal',
|
|
},
|
|
});
|
|
}
|
|
|
|
async createSystemTicket(
|
|
tenantId: string,
|
|
systemUserId: string,
|
|
data: {
|
|
clientId: string;
|
|
type: string;
|
|
title: string;
|
|
description?: string;
|
|
},
|
|
) {
|
|
return this.prisma.ticket.create({
|
|
data: {
|
|
tenantId,
|
|
clientId: data.clientId,
|
|
createdById: systemUserId,
|
|
type: data.type,
|
|
title: data.title,
|
|
description: data.description,
|
|
priority: 'high',
|
|
},
|
|
});
|
|
}
|
|
|
|
async update(tenantId: string, id: string, dto: UpdateTicketDto) {
|
|
const db = this.prisma.forTenant(tenantId);
|
|
const existing = await db.ticket.findFirst({ where: { id } });
|
|
|
|
if (!existing) {
|
|
throw new NotFoundException('Ticket not found');
|
|
}
|
|
|
|
return this.prisma.ticket.update({
|
|
where: { id },
|
|
data: {
|
|
...(dto.assigneeId !== undefined && { assigneeId: dto.assigneeId }),
|
|
...(dto.title && { title: dto.title }),
|
|
...(dto.description !== undefined && { description: dto.description }),
|
|
...(dto.priority && { priority: dto.priority }),
|
|
...(dto.status && { status: dto.status }),
|
|
...(dto.latitude !== undefined && { latitude: dto.latitude }),
|
|
...(dto.longitude !== undefined && { longitude: dto.longitude }),
|
|
},
|
|
});
|
|
}
|
|
|
|
async resolve(tenantId: string, id: string, resolvedById: string) {
|
|
const db = this.prisma.forTenant(tenantId);
|
|
const ticket = await db.ticket.findFirst({ where: { id } });
|
|
|
|
if (!ticket) {
|
|
throw new NotFoundException('Ticket not found');
|
|
}
|
|
|
|
if (ticket.status === 'resolved') {
|
|
throw new BadRequestException('Ticket is already resolved');
|
|
}
|
|
|
|
if (ticket.status === 'cancelled') {
|
|
throw new BadRequestException('Cannot resolve a cancelled ticket');
|
|
}
|
|
|
|
const resolved = await this.prisma.ticket.update({
|
|
where: { id },
|
|
data: {
|
|
status: 'resolved',
|
|
resolvedAt: new Date(),
|
|
assigneeId: resolvedById,
|
|
},
|
|
});
|
|
|
|
// Fire event for workflow automation
|
|
if (this.onTicketResolved && ticket.clientId) {
|
|
await this.onTicketResolved({
|
|
ticketId: id,
|
|
tenantId,
|
|
clientId: ticket.clientId,
|
|
type: ticket.type,
|
|
});
|
|
}
|
|
|
|
return resolved;
|
|
}
|
|
}
|