Merge dev to main #1
@@ -1,4 +1,5 @@
|
|||||||
import { IsString, IsOptional, IsIn, MinLength, IsUUID } from 'class-validator';
|
import { IsString, IsOptional, IsIn, MinLength, IsUUID, IsNumber } from 'class-validator';
|
||||||
|
import { Transform } from 'class-transformer';
|
||||||
|
|
||||||
export class UpdateTicketDto {
|
export class UpdateTicketDto {
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@@ -23,4 +24,14 @@ export class UpdateTicketDto {
|
|||||||
@IsString()
|
@IsString()
|
||||||
@IsIn(['open', 'in_progress', 'resolved', 'cancelled'])
|
@IsIn(['open', 'in_progress', 'resolved', 'cancelled'])
|
||||||
status?: string;
|
status?: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@Transform(({ value }) => value !== undefined && value !== null ? Number(value) : undefined)
|
||||||
|
@IsNumber()
|
||||||
|
latitude?: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@Transform(({ value }) => value !== undefined && value !== null ? Number(value) : undefined)
|
||||||
|
@IsNumber()
|
||||||
|
longitude?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
import { PrismaService } from '../prisma/prisma.service';
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
import { CreateTicketDto } from './dto/create-ticket.dto';
|
import { CreateTicketDto } from './dto/create-ticket.dto';
|
||||||
import { UpdateTicketDto } from './dto/update-ticket.dto';
|
import { UpdateTicketDto } from './dto/update-ticket.dto';
|
||||||
import { NotificationService } from '../notification/notification.service';
|
|
||||||
|
|
||||||
export interface TicketResolvedEvent {
|
export interface TicketResolvedEvent {
|
||||||
ticketId: string;
|
ticketId: string;
|
||||||
@@ -21,10 +20,7 @@ export class TicketService {
|
|||||||
onTicketResolved: ((event: TicketResolvedEvent) => Promise<void>) | null =
|
onTicketResolved: ((event: TicketResolvedEvent) => Promise<void>) | null =
|
||||||
null;
|
null;
|
||||||
|
|
||||||
constructor(
|
constructor(private readonly prisma: PrismaService) {}
|
||||||
private readonly prisma: PrismaService,
|
|
||||||
private readonly notificationService: NotificationService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async findAll(tenantId: string, filters?: { clientId?: string; status?: string; type?: string }) {
|
async findAll(tenantId: string, filters?: { clientId?: string; status?: string; type?: string }) {
|
||||||
const db = this.prisma.forTenant(tenantId);
|
const db = this.prisma.forTenant(tenantId);
|
||||||
@@ -115,22 +111,13 @@ export class TicketService {
|
|||||||
...(dto.description !== undefined && { description: dto.description }),
|
...(dto.description !== undefined && { description: dto.description }),
|
||||||
...(dto.priority && { priority: dto.priority }),
|
...(dto.priority && { priority: dto.priority }),
|
||||||
...(dto.status && { status: dto.status }),
|
...(dto.status && { status: dto.status }),
|
||||||
|
...(dto.latitude !== undefined && { latitude: dto.latitude }),
|
||||||
|
...(dto.longitude !== undefined && { longitude: dto.longitude }),
|
||||||
},
|
},
|
||||||
}).then(async (ticket) => {
|
|
||||||
// Notify on status change
|
|
||||||
if (dto.status) {
|
|
||||||
this.notificationService.create(tenantId, {
|
|
||||||
type: 'in_app',
|
|
||||||
channel: 'ticket_update',
|
|
||||||
title: 'Ticket Updated',
|
|
||||||
message: `Ticket "${existing.title}" status changed to ${dto.status.replace(/_/g, ' ')}`,
|
|
||||||
}).catch(() => {});
|
|
||||||
}
|
|
||||||
return ticket;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async resolve(tenantId: string, id: string, resolvedById: string, coords?: { latitude?: number; longitude?: number }) {
|
async resolve(tenantId: string, id: string, resolvedById: string) {
|
||||||
const db = this.prisma.forTenant(tenantId);
|
const db = this.prisma.forTenant(tenantId);
|
||||||
const ticket = await db.ticket.findFirst({ where: { id } });
|
const ticket = await db.ticket.findFirst({ where: { id } });
|
||||||
|
|
||||||
@@ -155,14 +142,6 @@ export class TicketService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update client coordinates if this is an installation ticket with coords
|
|
||||||
if (coords?.latitude !== undefined && coords?.longitude !== undefined && ticket.clientId && ticket.type === 'installation') {
|
|
||||||
await this.prisma.client.update({
|
|
||||||
where: { id: ticket.clientId },
|
|
||||||
data: { latitude: coords.latitude, longitude: coords.longitude },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fire event for workflow automation
|
// Fire event for workflow automation
|
||||||
if (this.onTicketResolved && ticket.clientId) {
|
if (this.onTicketResolved && ticket.clientId) {
|
||||||
await this.onTicketResolved({
|
await this.onTicketResolved({
|
||||||
@@ -173,14 +152,6 @@ export class TicketService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify on ticket resolution
|
|
||||||
this.notificationService.create(tenantId, {
|
|
||||||
type: 'in_app',
|
|
||||||
channel: 'ticket_update',
|
|
||||||
title: 'Ticket Resolved',
|
|
||||||
message: `Ticket "${ticket.title}" has been resolved`,
|
|
||||||
}).catch(() => {});
|
|
||||||
|
|
||||||
return resolved;
|
return resolved;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user