The Application Layer contains use cases that implement business operations. Each use case represents a single user action or business flow, orchestrating domain entities and repository interfaces to achieve a specific goal.Location:lib/aplicacion/
Handles reservation cancellation with different rules for customers vs. administrators.
lib/aplicacion/cancelar_reserva.dart
class CancelarReserva { final ReservaRepositorio reservaRepositorio; final NegocioRepositorio? negocioRepositorio; final MesaRepositorio? mesaRepositorio; final ServicioEmail? servicioEmail; CancelarReserva( this.reservaRepositorio, { this.negocioRepositorio, this.mesaRepositorio, this.servicioEmail, }); /// Cancel reservation by customer (with time restrictions) Future<void> ejecutar(String reservaId, {required String negocioId}) async { final reserva = await reservaRepositorio.obtenerReservaPorId(reservaId); if (reserva == null) { throw Exception('Reserva no encontrada'); } // Load business configuration int minHorasParaCancelar = 24; // Default String nombreNegocio = 'Restaurante'; String? emailDueno; if (negocioRepositorio != null) { final negocio = await negocioRepositorio!.obtenerNegocioPorId(negocioId); if (negocio != null) { minHorasParaCancelar = negocio.minHorasParaCancelar; nombreNegocio = negocio.nombre; emailDueno = negocio.email; } } // VALIDATION 1: Only confirmada/pendiente can be cancelled if (reserva.estado != EstadoReserva.confirmada && reserva.estado != EstadoReserva.pendiente) { throw Exception('Solo se pueden cancelar reservas confirmadas o pendientes.'); } // VALIDATION 2: Cannot cancel past reservations final ahora = DateTime.now(); if (reserva.fechaHora.isBefore(ahora)) { throw Exception('No se puede cancelar una reserva cuya hora ya pasó.'); } // VALIDATION 3: Must cancel with minimum advance notice final diferencia = reserva.fechaHora.difference(ahora); if (diferencia.inHours < minHorasParaCancelar) { throw Exception( 'Solo se puede cancelar con $minHorasParaCancelar horas de anticipación.' ); } // Cancel the reservation await reservaRepositorio.cancelarReserva(reservaId); // Send cancellation emails if (servicioEmail != null) { try { String nombreMesa = 'Mesa'; if (mesaRepositorio != null) { final mesa = await mesaRepositorio!.obtenerMesaPorId(reserva.mesaId); if (mesa != null) nombreMesa = mesa.nombre; } await servicioEmail!.notificarReservaCanceladaPorCliente( reserva, nombreNegocio: nombreNegocio, nombreMesa: nombreMesa, emailDueno: emailDueno, ); print('📧 Emails de cancelación enviados'); } catch (e) { print('⚠️ Error enviando email: $e'); } } } /// Cancel reservation by admin (no time restrictions) Future<void> ejecutarComoAdmin( String reservaId, { required String negocioId, String? motivo, }) async { final reserva = await reservaRepositorio.obtenerReservaPorId(reservaId); if (reserva == null) { throw Exception('Reserva no encontrada'); } // Admin can cancel without time restrictions await reservaRepositorio.cancelarReserva(reservaId); // Notify customer that restaurant cancelled if (servicioEmail != null) { try { String nombreNegocio = 'Restaurante'; String nombreMesa = 'Mesa'; if (negocioRepositorio != null) { final negocio = await negocioRepositorio!.obtenerNegocioPorId(negocioId); if (negocio != null) nombreNegocio = negocio.nombre; } if (mesaRepositorio != null) { final mesa = await mesaRepositorio!.obtenerMesaPorId(reserva.mesaId); if (mesa != null) nombreMesa = mesa.nombre; } await servicioEmail!.notificarReservaCanceladaPorRestaurante( reserva, nombreNegocio: nombreNegocio, nombreMesa: nombreMesa, motivo: motivo, ); print('📧 Email de cancelación por admin enviado'); } catch (e) { print('⚠️ Error enviando email: $e'); } } }}
Customer cancellation requires minHorasParaCancelar hours of advance notice (default 24h). Admin cancellation has no time restrictions but should include a reason.
class MyUseCase { final ReservaRepositorio reservaRepositorio; final OtroRepositorio? otroRepositorio; MyUseCase(this.reservaRepositorio, {this.otroRepositorio});}