feat(api): endpoint de suppression d une reservation non payee

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018BAUeCFpDkRD6tU5wGsc1C
This commit is contained in:
David 2026-09-07 21:40:53 +02:00
parent 607257f538
commit efc47aae67
2 changed files with 64 additions and 0 deletions

View File

@ -14,6 +14,7 @@ import {
BadRequestException, BadRequestException,
ForbiddenException, ForbiddenException,
ParseIntPipe, ParseIntPipe,
ParseUUIDPipe,
DefaultValuePipe, DefaultValuePipe,
Inject, Inject,
} from '@nestjs/common'; } from '@nestjs/common';
@ -591,6 +592,31 @@ export class CsvBookingsController {
return await this.csvBookingService.cancelBooking(id, userId); return await this.csvBookingService.cancelBooking(id, userId);
} }
/**
* Delete an unpaid booking
*
* DELETE /api/v1/csv-bookings/:id
*/
@Delete(':id')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@ApiOperation({
summary: 'Delete an unpaid booking',
description:
'Permanently deletes a booking whose commission has not been paid. Only accessible by the booking owner. A paid booking has been sent to the carrier and can only be cancelled.',
})
@ApiParam({ name: 'id', description: 'Booking ID (UUID)' })
@ApiResponse({ status: 200, description: 'Booking deleted successfully' })
@ApiResponse({ status: 400, description: 'Booking has been paid and cannot be deleted' })
@ApiResponse({ status: 404, description: 'Booking not found' })
@ApiResponse({ status: 401, description: 'Unauthorized' })
async deleteBooking(
@Param('id', ParseUUIDPipe) id: string,
@Request() req: any
): Promise<{ success: boolean; message: string }> {
return await this.csvBookingService.deleteBooking(id, req.user.id);
}
/** /**
* Update booking cargo details before payment * Update booking cargo details before payment
* *

View File

@ -1024,6 +1024,44 @@ export class CsvBookingService {
return this.toResponseDto(updatedBooking); return this.toResponseDto(updatedBooking);
} }
/**
* Delete an unpaid booking (user action).
*
* Seul le proprietaire peut supprimer, et seulement tant qu'aucun paiement
* n'a ete encaisse — voir `CsvBooking.isDeletable()`. Une reservation payee
* est partie chez le transporteur : elle s'annule, elle ne s'efface pas.
*
* Les documents deja televerses restent dans le stockage objet, comme lors de
* la suppression d'un document isole : la politique du projet est de les
* conserver pour l'audit.
*/
async deleteBooking(id: string, userId: string): Promise<{ success: boolean; message: string }> {
this.logger.log(`Deleting booking ${id} by user ${userId}`);
const booking = await this.csvBookingRepository.findById(id);
if (!booking) {
throw new NotFoundException('Booking not found');
}
// Meme reponse qu'une reservation inexistante : appartenir a quelqu'un
// d'autre ne doit pas etre distinguable de ne pas exister.
if (booking.userId !== userId) {
throw new NotFoundException('Booking not found');
}
if (!booking.isDeletable()) {
throw new BadRequestException(
`Cannot delete a booking with status ${booking.status}. Only unpaid bookings can be deleted.`
);
}
await this.csvBookingRepository.delete(id);
this.logger.log(`Booking ${id} deleted`);
return { success: true, message: 'Booking deleted successfully' };
}
/** /**
* Update the cargo details of a booking before payment (user action). * Update the cargo details of a booking before payment (user action).
* *