fix: resolve TypeORM cascade bug in ticket reply/close/updateStatus
- Replace ticketsRepo.save(ticket) with ticketsRepo.update() to avoid cascade orphaning existing messages (SET ticketId=null violates NOT NULL) - Fix reply(), close() in tickets.service.ts - Fix updateStatus() in tickets.controller.ts using new updateTicketFields() method - Add updateTicketFields() helper to TicketsService for safe partial updates
This commit is contained in:
@@ -107,11 +107,13 @@ export class TicketsController {
|
||||
@Request() req: any,
|
||||
@Body() dto: UpdateTicketStatusDto,
|
||||
) {
|
||||
const ticket = await this.ticketsService.findOne(id, req.user.id, req.user.role);
|
||||
ticket.status = dto.status;
|
||||
// Access check
|
||||
await this.ticketsService.findOne(id, req.user.id, req.user.role);
|
||||
const updateData: any = { status: dto.status };
|
||||
if (dto.status === TicketStatus.CLOSED) {
|
||||
ticket.closedAt = new Date();
|
||||
updateData.closedAt = new Date();
|
||||
}
|
||||
return this.ticketsService['ticketsRepo'].save(ticket);
|
||||
await this.ticketsService.updateTicketFields(id, updateData);
|
||||
return this.ticketsService.findOne(id, req.user.id, req.user.role);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,24 +85,20 @@ export class TicketsService {
|
||||
|
||||
// ─── Reply to ticket ───
|
||||
async reply(ticketId: string, userId: string, userRole: UserRole, dto: ReplyTicketDto): Promise<TicketMessage> {
|
||||
const ticket = await this.findOne(ticketId, userId, userRole);
|
||||
// Verify access first
|
||||
await this.findOne(ticketId, userId, userRole);
|
||||
|
||||
const msg = this.messagesRepo.create({
|
||||
ticketId: ticket.id,
|
||||
ticketId,
|
||||
senderId: userId,
|
||||
senderRole: userRole,
|
||||
message: dto.message,
|
||||
});
|
||||
const saved = await this.messagesRepo.save(msg);
|
||||
|
||||
// Update ticket status based on who replied
|
||||
if (userRole === UserRole.USER) {
|
||||
ticket.status = TicketStatus.WAITING;
|
||||
} else {
|
||||
// Staff or admin replied
|
||||
ticket.status = TicketStatus.ANSWERED;
|
||||
}
|
||||
await this.ticketsRepo.save(ticket);
|
||||
// Update ticket status directly (avoid cascade issues with loaded relations)
|
||||
const newStatus = userRole === UserRole.USER ? TicketStatus.WAITING : TicketStatus.ANSWERED;
|
||||
await this.ticketsRepo.update(ticketId, { status: newStatus });
|
||||
|
||||
this.logger.log(`Reply on ticket ${ticketId} by ${userRole} user ${userId}`);
|
||||
return saved;
|
||||
@@ -110,10 +106,17 @@ export class TicketsService {
|
||||
|
||||
// ─── Close ticket ───
|
||||
async close(ticketId: string, userId: string, userRole: UserRole): Promise<Ticket> {
|
||||
const ticket = await this.findOne(ticketId, userId, userRole);
|
||||
ticket.status = TicketStatus.CLOSED;
|
||||
ticket.closedAt = new Date();
|
||||
return this.ticketsRepo.save(ticket);
|
||||
await this.findOne(ticketId, userId, userRole);
|
||||
await this.ticketsRepo.update(ticketId, {
|
||||
status: TicketStatus.CLOSED,
|
||||
closedAt: new Date(),
|
||||
});
|
||||
return this.findOne(ticketId, userId, userRole);
|
||||
}
|
||||
|
||||
// ─── Update ticket fields (safe, no cascade) ───
|
||||
async updateTicketFields(ticketId: string, data: Partial<Ticket>): Promise<void> {
|
||||
await this.ticketsRepo.update(ticketId, data);
|
||||
}
|
||||
|
||||
// ─── Staff: List department tickets ───
|
||||
|
||||
Reference in New Issue
Block a user