Boarding, payment methods, journey direction on seat hold, and more updates

This commit is contained in:
Stephanos A
2026-06-29 08:44:38 +03:00
parent 81ae99cee3
commit c6e56d1c4f
65 changed files with 6437 additions and 1425 deletions

View File

@@ -0,0 +1,41 @@
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common';
import { Response } from 'express';
import { DeleteOperationException } from './delete-operation.exception';
@Catch(DeleteOperationException, HttpException)
export class DeleteExceptionFilter implements ExceptionFilter {
catch(exception: DeleteOperationException | HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const status = exception.getStatus?.() || HttpStatus.BAD_REQUEST;
if (exception instanceof DeleteOperationException) {
// Format the response specifically for delete operations
response.status(status).json({
statusCode: status,
error: 'Delete Operation Failed',
message: exception.message,
timestamp: new Date().toISOString(),
type: 'DELETE_CONSTRAINT_VIOLATION',
userFriendly: true,
details: {
canRetry: true,
action: 'RESOLVE_DEPENDENCIES',
hint: 'Please resolve the listed dependencies and try again.'
}
});
} else if (exception instanceof HttpException) {
// Handle other HTTP exceptions normally
const exceptionResponse = exception.getResponse();
response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
...(typeof exceptionResponse === 'object'
? exceptionResponse
: { message: exceptionResponse }
)
});
}
}
}

View File

@@ -0,0 +1,85 @@
import { BadRequestException } from '@nestjs/common';
export interface DeleteConstraint {
entityName: string;
count: number;
action: 'delete' | 'reassign' | 'cancel' | 'complete';
}
export class DeleteOperationException extends BadRequestException {
constructor(
entityType: string,
entityName: string,
constraints: DeleteConstraint[]
) {
const message = DeleteOperationException.buildUserFriendlyMessage(
entityType,
entityName,
constraints
);
super(message);
}
private static buildUserFriendlyMessage(
entityType: string,
entityName: string,
constraints: DeleteConstraint[]
): string {
const baseMessage = `Cannot delete ${entityType.toLowerCase()} "${entityName}".`;
if (constraints.length === 0) {
return `${baseMessage} Unknown constraint violation.`;
}
const constraintMessages = constraints.map(constraint => {
const { entityName: constraintEntity, count, action } = constraint;
const entityDisplayName = count === 1
? constraintEntity.toLowerCase()
: `${constraintEntity.toLowerCase()}s`;
const actionText = this.getActionText(action, count);
return `${count} ${entityDisplayName} ${count === 1 ? 'is' : 'are'} still ${this.getStatusText(constraintEntity)}. Please ${actionText} first.`;
});
return [
baseMessage,
'',
'The following dependencies must be resolved:',
...constraintMessages,
'',
'Once all dependencies are resolved, you can retry the deletion.'
].join('\n');
}
private static getActionText(action: string, count: number): string {
const actions: Record<string, string> = {
delete: count === 1 ? 'delete it' : 'delete them',
reassign: count === 1 ? 'reassign it' : 'reassign them',
cancel: count === 1 ? 'cancel it' : 'cancel them',
complete: count === 1 ? 'complete it' : 'complete them'
};
return actions[action] || (count === 1 ? 'resolve it' : 'resolve them');
}
private static getStatusText(entityName: string): string {
const statusTexts: Record<string, string> = {
booking: 'active',
schedule: 'in use',
coach: 'assigned',
seat: 'occupied or blocked',
'seat class': 'in use by fare rules',
'coach type': 'in use by coaches or seat classes',
train: 'scheduled',
ticket: 'issued',
'payment record': 'linked',
'fare rule': 'active',
route: 'in use by schedules',
passenger: 'active with bookings or accounts',
promotion: 'active',
station: 'in use by routes'
};
return statusTexts[entityName.toLowerCase()] || 'in use';
}
}

View File

@@ -0,0 +1,2 @@
export { DeleteOperationException, DeleteConstraint } from './delete-operation.exception';
export { DeleteExceptionFilter } from './delete-exception.filter';