| INFORMACIÓN BÁSICA | |||||
|---|---|---|---|---|---|
| ASIGNATURA: | Programación Web 2 | ||||
| SEMESTRE: | III | ||||
| ESTUDIANTE: | Juan José Condori Pinto (jcondoripin@unsa.edu.pe) | ||||
| FECHA INICIO: | 01-Ago-2022 | FECHA FIN: | 05-Ago-2022 | DURACIÓN: | 04 horas |
Se trata de una aplicación web construida con el framework Django 4, que permite la gestión básica del proceso de recepción de un hotel, desde la reserva de los huespedes hasta la salida de los mismos, la aplicación cuenta con dos vistas: para gerente y para recepcionista.
Requerimientos funcionales
-
RQ01 :
- Titulo: Inicio y cierre de sesión
- Descripción: El sistema debe permitir el inicio de sesión a partir de un email y una contraseña, así como el cierre de sesión.
- RNF:
- RNF01
- RNF03
- RNF04
- RNF05
-
RQ02 :
- Titulo: Division de trabajo por roles
- Descripción: El sistema debe permitir la division de responsabilidades por roles (gerente y recepcionista), designando permisos y tareas para cada uno
- RNF:
- RNF01
- RNF03
- RNF04
-
RQ03:
- Titulo: Gestion de habitaciones
- Descripción: El sistema debe autorizar al gerente la gestión de habitaciones, tanto sea creación, edición, listado (también para los recepcionistas) y eliminación, tomando datos como número de habitación, piso, tipo, precio por noche, tamaño, imagen (en caso posea) y contenido de la misma.
- RNF:
- RNF01
- RNF02
- RNF03
- RNF04
-
RQ04:
- Titulo: Gestion de recepcionistas
- Descripción: El sistema debe autorizar al gerente la gestión de los recepcionistas de su hotel, registro, remoción, edición y listado, sus datos serían nombres, apellidos, dni, telefono, turno, email y contraseña del usuario.
- RNF:
- RNF01
- RNF03
- RNF04
-
RQ05:
- Titulo: Visualización de reportes
- Descripción: El sistema debe mostrar al gerente datos reales del estado actual de su empresa, tanto sea número de recepcionistas, reservas, habitaciones, checkins por habitación, huespedes por nacionalidad y ventas semanales.
- RNF:
- RNF02
- RNF04
-
RQ06:
- Titulo: Gestion de reservas
- Descripción: El sistema debe poder permitirle a los recepcionistas la generación de reservas, pidiendo datos del huesped (identificacion, nombres, apellidos, sexo, fecha de nacimiento, nacionalidad, región, telefono y RUC de empresa, en caso posea), además del número de habitación que se desea reservar y los datos de llegada.
- RNF:
- RNF01
- RNF02
- RNF03
- RNF04
- RNF05
-
RQ07:
- Titulo: Gestión de huespedes
- Descripción: El recepcionista debe poder manejar la información de los huespedes, poder editarla y borrarlo en caso sea necesario, además de generar los acompañantes que posea para su hospedaje.
- RNF:
- RNF01
- RNF02
- RNF03
- RNF04
-
RQ08:
- Titulo: Manejo de Checkins
- Descripción: El recepcionista debe poder manejar el registro de checkins al momento de la llegada de los huespedes, estos se deberían de generar a partir de una reserva indicando los paxx (cantidad de acompañantes), fecha y en qué estado se encuentra (Activo - inactivo)
- RNF:
- RNF01
- RNF03
- RNF04
-
RQ09:
- Titulo: Manejo de checkouts
- Descripción: El recepcionista debe poder manejar el registro de checkouts al momento de que los huespedes se retiren del hotel o su hospedaja haya terminado, mostrando total calculada del titular
- RNF:
- RNF01
- RNF03
- RNF04
-
RQ10:
- Titulo: Manejo de recordatorios
- Descripción: El sistema puede permitir el manejo de etiquetas de recordatorio para los usuarios (gerente y recepcionistas) en tanto sea necesario, contemplando un título y descripción.
- RNF:
- RNF03
- RNF04
Requerimientos no funcionales
- RNF01 :
- Titulo: Seguridad de información
- Descripcón: El sistema debe encapsular los datos tanto de usuarios como información sensible de la base de datos solo accesibles a partir de métodos de autenticación.
- RNF02 :
- Titulo: Velocidad y tiempo de respuesta
- Descripción: El sistema debe tener un tiempo de respuesta mínimo ante las solicitudes y manejo de eventos.
- RNF03 :
- Titulo: Interfaz intuitiva
- Descripcion: La interfaz debe ser amigable con el usuario y accesible a todo público, sin redundancia o contenido inutilizable.
- RNF04 :
- Titulo: Disponibilidad continua
- Descripcion: La plataforma debe estar disponible en todo momento para el usuario.
- RNF05 :
- Titulo: Manejo de errores
- Descripcion: El sistema debe tolerar y ser capaz de manejar los errores que se vayan produciendo por parte del mal manejo de datos y falta de autenticación.
El modelo de datos esta conformado por las siguientes entidades.
-
Administrador (usuario): Es el usuario autenticable de la base de datos, este posee dos roles (gerente y recepcionista), atraves del cual será posible el acceso al dashboard de la aplicación.
-
Habitación: En esta entidad se almacena la información de las habitaciones generadas por el gerente, las reservas se generan en torno a estas entidades.
-
Contenido: Esta entidad almacena la información de un contenido que posea una habitación, estas últimas pueden poseer más de un contenido: cama, televisión, escritorio, etc.
-
Huesped: Esta representa al huesped que se hospedará en el hotel (titular), almacena datos básicos de la persona y puede poseer más de una reserva,
-
Acompañante: Un huesped puede poseer más de un acompañante, a diferencia de un huesped en esta entidad se almacenan datos más reducidos de identificación.
-
Reserva: Esta entidad es la base del proceso de recepción, almacena datos de la reserva que genera el recepcionista para un huesped, posee un estado dinámico a medida que continua el proceso de recepción.
-
Checkin: Esta entidad sirve como registro para el momento en el que un huesped haya ingresado a su estadía o se encuentre dentro del hotel, cuando ocurre esto el estado de su reserva pasa a 'Registrado'.
-
Checkout: Esta entidad sirve como registro para el momento en el que el huesped se retira del hotel, está enlazado a un solo checkin, al igual que este a una reserva.
-
Remind: Esta entidad es independiente y contiene información sobre un recordatorio que el usuario desee generar (titulo y descripción), un usuario puede poseer más de un recordatorio.
En la construcción de software y en el diccionario de datos se tomará en cuenta el campo 'id' autogenerado de cada tabla como llave primaria.
| Administrador | |||||
|---|---|---|---|---|---|
| Atributo | Tipo | Nulo | Unico | Predeterminado | Descripción |
| Cadena | No | Si | Ninguno | Email del usuario | |
| nombres | Cadena | No | No | Ninguno | Nombre del usuario |
| apellidos | Cadena | No | No | Ninguno | Apellidos del usuario |
| dni | Numerico | No | Si | Ninguno | DNI del usuario |
| telefono | Numerico | No | Si | Ninguno | Telefono del usuario |
| rol | Cadena | No | No | Recepcionista | Rol del usuario |
| turno | Cadena | Si | No | NULL | Turno del recepcionista |
Para el resto de tablas se consideran los campos created_at y updated_at como Date.
| Habitación | |||||
|---|---|---|---|---|---|
| Atributo | Tipo | Nulo | Unico | Predeterminado | Descripción |
| nro_habitacion | Numerico | No | Si | Ninguno | Nro de habitación |
| nro_piso | Numerico | No | No | Ninguno | Nro de piso |
| tipo_habitacion | Cadena | No | No | Ninguno | Tipo de habitación |
| precio | Decimal | No | No | Ninguno | Precio por noche |
| estado | Cadena | No | No | Libre | Estado de habitación |
| tamanio | Decimal | Si | No | Ninguno | Tamaño de habitación |
| imagen | Image | Si | No | NULL | Imagen de habitación |
| Contenido | |||||
|---|---|---|---|---|---|
| Atributo | Tipo | Nulo | Unico | Predeterminado | Descripción |
| habitacion_id | Foraneo: Habitacion | No | No | Ninguno | Llave foránea de habitación |
| nombre | Cadena | No | No | Ninguno | Nombre de contenido |
| cantidad | Numerico | No | No | Ninguno | Cantidad del contenido |
| descripcion | Cadena | Si | No | NULL | Descripción del contenido |
| Huesped | |||||
|---|---|---|---|---|---|
| Atributo | Tipo | Nulo | Unico | Predeterminado | Descripción |
| tipo_identificacion | Cadena | No | No | Dni | Tipo de identificación del huesped (DNI - Carnet) |
| identificacion | Cadena | No | Si | Ninguno | Nro de identificacion |
| nombres | Cadena | No | No | Ninguno | Nombres de huesped |
| apellidos | Cadena | No | No | Ninguno | Apellidos de huesped |
| sexo | Cadena | No | No | Ninguno | Sexo de huesped |
| fecha_nacimiento | Date | Si | No | NULL | Fecha de nacimiento del huesped |
| nacionalidad | Cadena | No | No | Ninguno | Nacionalidad del huesped |
| region | Cadena | No | No | Ninguno | Región dependiendo nacionalidad |
| telefono | Numerico | Si | No | NULL | Telefono de huesped |
| hospedado | Boolean | No | No | False | Estado de huesped (hospedado o no) |
| ruc_empresa | Cadena | Si | No | NULL | Ruc en caso posea a una empresa |
| Acompanante | |||||
|---|---|---|---|---|---|
| Atributo | Tipo | Nulo | Unico | Predeterminado | Descripción |
| titular_id | Foraneo | No | No | Ninguno | ID del titular al que acompaña |
| nombres | Cadena | No | No | Ninguno | Nombres de acompañante |
| apellidos | Cadena | No | No | Ninguno | Apellidos de acompañante |
| sexo | Cadena | No | No | Ninguno | Sexo de acompañante |
| tipo_identificacion | Cadena | No | No | Ninguno | Tipo de identificacion (Dni - carnet) |
| identificacion | Cadena | No | Si | Ninguno | Nro de identificación |
| relacion | Cadena | No | No | Ninguno | Relación con titular |
| Reserva | |||||
|---|---|---|---|---|---|
| Atributo | Tipo | Nulo | Unico | Predeterminado | Descripción |
| recepcionista_id | Foraneo | No | No | Ninguno | Recepcionista quien creó la reserva |
| titular_id | Foraneo | Si | No | NULL | ID del titular a quien pertenece |
| cantidad_dias | Numerico | No | No | Ninguno | Cantidad de días de hospedaje |
| tipo_reserva | Cadena | No | No | presencial | (Trabajo futuro) Medio por el cual se generó la reserva (presencial o virtual) |
| razon_hospedaje | Cadena | No | No | Ninguno | Razón por la cual se hospeda |
| peticiones | Texto | Si | No | NULL | Peticiones que tenga el huesped |
| fecha_llegada | Date | No | No | Ninguno | Fecha de llegada del huesped |
| estado | Cadena | No | No | Pendiente | Estado de reserva, sea pendiente, registrado, cancelado, pasado o registrado |
| habitacion_id | Foraneo | No | No | Ninguno | Habitación que se reserva |
| Checkin | |||||
|---|---|---|---|---|---|
| Atributo | Tipo | Nulo | Unico | Predeterminado | Descripción |
| recepcionista_id | Foraneo | Si | No | NULL | Recepcionista quien lo generó |
| reserva_id | Foraneo 1-1 | No | Si | Ninguno | Reserva a la cual pertenece el checkin |
| fecha_entrada | DateTime | No | No | Ahora | Fecha de llegada para el huesped |
| estado | Cadena | No | No | Activo | Estado de checkin (activo - inactivo) |
| paxx | Numerico | No | No | 0 | Cantidad de acompañantes |
| Checkout | |||||
|---|---|---|---|---|---|
| Atributo | Tipo | Nulo | Unico | Predeterminado | Descripción |
| checkin_id | Foraneo 1-1 | Si | No | NULL | Recepcionista quien lo generó |
| fecha_salida | DateTime | No | No | Ahora | Fecha de cuando se retiró el huesped |
| descripcion_salida | Cadena | Si | No | NULL | Descripción de la salida del cliente (reseña) |
| tarifa | Decimal | No | No | Ninguno | Tarifa por cantidad de días y precio de habitación por noche |
| Remind | |||||
|---|---|---|---|---|---|
| Atributo | Tipo | Nulo | Unico | Predeterminado | Descripción |
| titulo | Cadena | No | No | Ninguno | Titulo de recordatorio |
| descripcion | Cadena | No | No | Ninguno | Descripción de recordatorio |
| usuario_id | Foraneo | No | No | Ninguno | Usuario al cual pertenece |
Se muestran los pasos realizados para crear el Proyecto, la aplicación, creacion de modelos, migraciones y habilitación del panel de administración en Django.
La creación de un proyecto en django se realiza mediante el siguiente comando:
django-admin startproject FastBooking .
Para la aplicación presentada, el proyecto se llama 'FastBooking' y consta de una aplicación 'Api'
django-admin startapp Api
La aplicación de django no consta de una interfaz personalizada, sino la que nos proporciona por defecto el panel de administrador.
Los modelos estarán contenidos dentro de models.py, sin embargo, debido a que el usuario puede poseer dos roles ('gerente' y 'recepcionista'), se está optando por generar un usuario customizado de la siguiente manera:
class CustomUserManager(UserManager):
def _create_user(self, email, password, **extra_fields):
if not email:
raise ValueError("Debes proporcionar un email valido")
email = self.normalize_email(email)
user = self.model(email=email,**extra_fields)
user.set_password(password) user.save(using=self.db)
return user
def create_user(self, email=None, password=None, **extra_fields):
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
return self._create_user(email, password, **extra_fields)
def create_superuser(self, email=None, password=None, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
extra_fields.setdefault('rol', 'gerente')
return self._create_user(email, password, **extra_fields)
La clase presentada es la base de un usuario autenticado, en ella se definen los métodos básicos de creación
class User(AbstractBaseUser, PermissionsMixin):
roles = [
('recepcionista', 'recepcionista'),
('gerente', 'gerente')
]
turnos = [
('mañana', 'mañana'),
('tarde', 'tarde'),
('noche', 'noche'),
('finesSemana', 'finesSemana'),
]
email = models.EmailField(blank=True, default='', unique=True)
nombres = models.CharField(max_length=255)
apellidos = models.CharField(max_length=255)
dni = models.PositiveIntegerField(unique=True)
telefono = models.PositiveIntegerField(unique=True)
rol = models.CharField(max_length=15, choices=roles, default='recepcionista')
turno = models.CharField(max_length=15, choices=turnos)
is_active = models.BooleanField(default=True)
is_superuser = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
date_joined = models.DateTimeField(default=timezone.now)
last_login = models.DateTimeField(blank=True, null=True)
objects = CustomUserManager()
USERNAME_FIELD = 'email'
EMAIL_FIELD = 'email'
REQUIRED_FIELDS = ['nombres', 'apellidos', 'dni', 'telefono']
class Meta:
db_table = 'administradores'
verbose_name = 'administrador'
verbose_name_plural = 'administradores'
def get_full_name(self):
return self.nombres
def get_short_name(self):
return self.nombres or self.email.split('@')[0]
La clase de usuario posee algunos campos adicionales, como rol, turno, telefono o dni, además de definir el atributo objects con el manager definido anteriormente
class Habitacion(models.Model):
estados = [
('Libre', 'Libre'),
('Ocupado', 'Ocupado'),
('Reservado', 'Reservado'),
('Limpieza', 'Limpieza'),
('No operativo', 'NoOperativo'),
]
nro_habitacion = models.PositiveIntegerField(unique=True, null=False)
nro_piso = models.PositiveSmallIntegerField(null=False)
tipo_habitacion = models.CharField(null=False, max_length=30)
precio = models.DecimalField(max_digits=6, decimal_places=2, null=False)
estado = models.CharField(choices=estados, default='Libre', max_length=12)
tamanio = models.DecimalField(max_digits=4, decimal_places=2)
imagen = models.ImageField(upload_to='habitaciones', null=True)
created_at = models.DateTimeField(auto_now_add=True, null=False)
updatedat = models.DateTimeField(auto_now=True)
class Contenido(models.Model):
habitacion = models.ForeignKey(Habitacion, on_delete=models.CASCADE, null=False)
nombre = models.CharField(max_length=100, null=False)
cantidad = models.PositiveIntegerField(default=1, null=False)
descripcion = models.CharField(max_length=100, null=True)
created_at = models.DateTimeField(auto_now_add=True, null=False)
updated_at = models.DateTimeField(auto_now=True)
class Huesped(models.Model):
tipos_identificacion = [
('Dni', 'Dni'),
('Carnet_Extranjeria', "Carnet_Extranjeria")
]
tipo_identificacion = models.CharField(max_length=20, choices=tipos_identificacion, default='Dni')
identificacion = models.CharField(max_length=20, null=False, unique=True)
nombres = models.CharField(max_length=50, null=False)
apellidos = models.CharField(max_length=50, null=False)
sexo = models.CharField(choices=[('Masculino', 'masculino'), ('Femenino', 'femenino')], null=False, max_length=9)
fecha_nacimiento = models.DateField(auto_created=False)
nacionalidad = models.CharField(max_length=20, null=False)
region = models.CharField(max_length=30)
telefono = models.PositiveIntegerField()
hospedado = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True, null=False)
updated_at = models.DateTimeField(auto_now=True)
# Opcional, si el huesped viaja por motivos de negocio
ruc_empresa = models.CharField(null=True, max_length=11, blank=True)
class Acompanante(models.Model):
titular = models.ForeignKey(Huesped, on_delete=models.CASCADE)
nombres = models.CharField(max_length=50)
apellidos = models.CharField(max_length=50)
sexo = models.CharField(choices=[('Masculino', 'masculino'), ('Femenino', 'femenino')], null=False, max_length=9)
tipo_identificacion = models.CharField(max_length=20, null=True)
identificacion = models.CharField(max_length=20, null=True, unique=True)
relacion = models.CharField(max_length=50, help_text="Relación con el titular")
created_at = models.DateTimeField(auto_now_add=True, null=False)
updated_at = models.DateTimeField(auto_now=True)
class Reserva(models.Model):
estados = [
('Pendiente', 'pendiente'), # Aún ho hay checkin
('Cancelado', 'cancelado'),
('Pasado', 'pasado'), # Se realizó el checkout
('Registrado', 'Registrado') # Cuando se realizó el checkin
]
# Para saber cuál recepcionista lo realizó
recepcionista = models.ForeignKey(User, on_delete=models.SET_NULL, name='recepcionista', null=True)
titular = models.ForeignKey(Huesped, on_delete=models.CASCADE, null=False)
cantidad_dias = models.PositiveIntegerField(null=False)
tipo_reserva = models.CharField(choices=[('Presencial', 'presencial'), ('Virtual', 'virtual')], default='presencial', max_length=15)
razon_hospedaje = models.CharField(max_length=50)
peticiones = models.TextField(max_length=100, blank=True, null=True)
fecha_llegada = models.DateField(null=False)
estado = models.CharField(choices=estados, default='Pendiente', max_length=10)
habitacion = models.ForeignKey(Habitacion, on_delete=models.CASCADE, null=False)
created_at = models.DateTimeField(auto_now_add=True, null=False)
updated_at = models.DateTimeField(auto_now=True)
class Checkin(models.Model):
estados = [
('Activo', 'activo'), # Checkin vigente
('Inactivo', 'inactivo') # Checkout realizado
]
# Para saber cuál recepcionista lo realizó
recepcionista = models.ForeignKey(User, on_delete=models.SET_NULL, name='recepcionista', null=True)
reserva = models.OneToOneField(Reserva,on_delete=models.CASCADE)
fecha_entrada = models.DateTimeField(auto_now_add=True, null=False)
estado = models.CharField(choices=estados, default='Activo', max_length=8)
paxx = models.PositiveIntegerField(default=0) # Número de acompañantes
created_at = models.DateTimeField(auto_now_add=True, null=False)
updated_at = models.DateTimeField(auto_now=True)
class Checkout(models.Model):
# Para saber cuál recepcionista lo realizó
recepcionista = models.ForeignKey(User, on_delete=models.SET_NULL, name='recepcionista', null=True)
checkin = models.OneToOneField(Checkin, on_delete=models.CASCADE)
fecha_salida = models.DateTimeField(auto_now_add=True, null=False)
descripcion_salida = models.CharField(blank=True, null=True, max_length=500)
tarifa = models.DecimalField(decimal_places=2, max_digits=7, null=False)
created_at = models.DateTimeField(auto_now_add=True, null=False)
updated_at = models.DateTimeField(auto_now=True)
class Remind(models.Model):
titulo = models.CharField(max_length=50, null=False)
descripcion = models.CharField(max_length=255, null=False)
usuario = models.ForeignKey(User, on_delete=models.CASCADE, null=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
Note que cada clase contiene dos campos created_at y updated_at, estos no fueron detallados en el diccionario de datos debido a que son comunes para casi todas las tablas.
Para migrar las tablas a la base de datos es necesario primero crear las migraciones y ejecutarlas a través de los comandos:
python manage.py makemigrations
python manage.py migrate
Luego de ello debemos registrar un superusuario (gerente) para que este pueda generar los recepcionistas y gestionar sus cuentas
python manage.py createsuperuser
Si los modelos se colocaron de forma correcta, para la creación del superusuario debería pedir email, nombres, apellidos, telefono, dni y contraseña.
Una vez registrado debería registrarse al menos un modelo en el panel de administrador, en nuestro caso se optó solo por el manejo de usuarios, mientras que las demás tablas serán gestionadas en el dashboard.
from django.contrib import admin
from .models import User
admin.site.register(User)
Una vez colocado, en el panel de administrador nos debería de aparecer algo parecido a esto:
El front end fue desarrollado en Angular v16 mediante el uso de componentes, rutas, guards, servicios e interfaces Typescript.
El usuario final estaría destinado para el gerente y los recepcionistas del hotel, por lo tanto, se optó por la creación de dos vistas de dashboard, una para gerente:
Y otra para el recepcionista:
Ambas vistas cuentan con el cambio de tema a oscuro, contracción de sidebar y generación de recordatorios.
El gerente tiene la potestad de generar habitacioens y los recepcionistas que pertenecerán a su hotel:
Como se logra observar, se pueden generar, eliminar y también buscar habitaciones según un campo especificado en el header de la tabla
Esta es la visualización de una habitación en específico, se pueden editar los campos y el contenido, la misma plantilla sirve para generar más habitaciones.
Lo más importante para el gerente es la generación de recepcionistas para su hotel, estos se contemplan en la siguiente vista:
Se pueden generar y eliminar, también buscar por campo
Esta vista sirve para la modificación de datos, también para el registro de recepcionistas.
En esta sección se muestran algunos datos puntuales, como por ejemplo el número de recepcionistas actuales, reservas, habitaciones, checkins por habitación, huespedes por nacionalidad y ventas semanales, para el ejemplo se realizó una reserva y el checkout correspondiente, por lo cual, una habitación posee un checkin y un huesped fue registrado.
Como en el ejemplo anterior, se encuentra un huesped registrado y cada fila de registro posee 3 funciones: eliminar, editar y generar reserva, un huesped que se encuentra ya registrado puede generar otra reserva si esque desea volver al hotel, dicha vista se mostrará más adelante.
En esta vista se pueden editar los campos del huesped registrado, además de registrar a sus acompañantes (esto solo es posible antes de realizar su checkin).
La vista de habitaciones para recepcionista solo le permite visualizar el listado y poder modificar el estado en el que se encuentran.
La vista de reservas le permite a lrecepcionista visualizar un listado, genear, editar, cancelar, eliminar y hacer un checkin de reservas.
En esta sección se puede editar la reserva seleccionada, la fecha de llegada, cantidad de días que se quedará el huesped, sus peticiones y la habitación a reservar.
Cuando se genera una reserva se deben ingresar los datos del tituloar (huesped), además de los datos mostrados en la anterior sección, cabe decir que en el listado de huéspedes también se puede llegar a esta vista, rellenando automaticamente los datos del huesped.
La sección de checkins se muestra a manera de listado, pudiendo eliminarlos (en cuyo caso volvería al estado de reserva) o generar un checkout
En la sección de checkouts se muestra solo un listado de registros, aquí aparece la tarifa que se tiene que cobrar por hospedaje y la descripción de salida; si se borra, el checkin al cual pertenece volverá a tener un estado activo
La funcionalidad de recordatorios puede ser utilizada tanto por el gerente como el recepcionista, ambos generan un recordatorio en caso requieran.
El proyecto de angular consta de componentes (porciones de código HTML con independencia), guards (guardias para el bloqueo de acceso a rutas de dashboard, por autenticación y por rol), interfaces (tipo clases, permiten el modelado y estructuración de un tipo de dato, tanto para enviar como para recibir las respuestas de las apis), servicios (bloques de funcionalidad injectables a tantos componentes como se requiera, poseen la comunicación con el backend) y views, siendo estas últimas las vistas principales del proyecto, por si solas implican un bloque significativo y pueden contener más de un componente
Esta estructura corresponde a las vistas, debido a que algunas rutas son anidadas, como por ejemplo: 'dashboard/gerente' - 'dashboard/recepcionista', es necesario el uso de routes a manera de componentes, estos actúan como punto de articulación para el redireccionamiento a distintas vistas, un ejemplo de ello es 'dash-board-view'
La comunicación se logra mediante las peticiones a las apis del backend, gracias a la librerías HttpClient de Angular, mediante esta clase se crea un observable, a partir del cual se envía la petición y se espera la respuesta, por ejemplo:
export class LoginService extends ApiService{
private apiUrl : string = `${this.HOST_API}${this.API_AUTH}`;
private apiUrlLogout : string = `${this.HOST_API}${this.API_BACK}auth/`;
login(user : User) {
return this.httpClient.post(`${this.apiUrl}`, user)
}
logout() {
return this.httpClient.post(this.apiUrlLogout, null, { headers: this.getHeaders() })
}
}
Este servicio de login hereda de una super clase ApiService, en esta misma se encuentra la url del backend y los headers de autenticación:
export abstract class ApiService { // Servicio global para apis
HOST_API: string = 'http://localhost:8000'
API_AUTH: string = '/api-token-auth/'
API_BACK: string = '/api/v1/'
protected HEADER: HttpHeaders = new HttpHeaders();
constructor(protected httpClient : HttpClient, private storage : StorageInfoService) { }
getHeaders() : HttpHeaders {
return this.HEADER.append('Authorization', `Token ${this.storage.getKeyToken()}`);
}
}
Estas clases pueden ser instanciadas a manera de atributo en los componentes y llamar a sus métodos para la comunicación con las apis, ejemplo:
constructor(private service : LoginService, private storage : StorageInfoService, private route : Router ) { }
login() {
if (!this.formData.invalid) {
this.error = '';
this.errorEmail = false;
this.errorPassword= false;
this.service.login(this.formData.value as User).subscribe({
next: ((res : any) => { // Response ok
this.storage.setInfo(res);
this.route.navigate([`dashboard/${this.storage.getRol()}/`]); // Redireccion a ruta segun rol
}),
error: (err : any) => { // Error
// Mensajes de error en vista
this.error = 'Credenciales incorrectas';
this.errorEmail = true;
this.errorPassword= true;
}
});
}
}
El núcleo de la aplicación redica en el manejo del proceso de recepción, el cliente final es el usuario (gerente y recepcionista) del hotel, el proceso básico de recepción cosnta de:
- El recepcionista inicia sesión en la aplicación.
- El cliente llega al hotel y pide una reserva.
- El recepcionista registra los datos del huesped y de su reserva, visualiza las habitaciones disponibles.
- Se genera la reserva y antes de realizar el checkin, se deben de registrar los acompañantes.
- Una vez generados los acompañantes y el huesped haya llegado para su hospedaje, se realiza el checkin.
- Pasado el tiempo de estadía, el huesped se retira, a esto último se le conoce como checkout.
La estructura de las plantillas para el front end de la aplicación se explicó anteriormente, así como la creación de los modelos.
Se créo un servicio API RESTful para el manejo de la información del hotel, este consta de varias apis, en cada una es necesario el manejo de por lo menos un modelo y un serializer, este último sirve para comprimir la información de un objeto obtenido de la base de datos a texto el cual pueda interpretar el navegador.
Fue creado un serializer por cada modelo, en él se incluyen los campos a serializar, aquellos de lectura y el modelo:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'nombres', 'apellidos', 'dni', 'telefono', 'turno', 'email')
read_only_fields = ['rol', 'created_at', 'updated_at']
def create_user(self, validated_data):
User.objects.create_user(**validated_data)
def update_user_password(self, validated_data, id):
if 'password' in validated_data:
user = User.objects.get(pk=id)
user.set_password(validated_data['password'])
user.save()
class HabitacionSerializer(serializers.ModelSerializer):
class Meta:
model = Habitacion
fields = ('id', 'nro_habitacion', 'nro_piso', 'tipo_habitacion', 'precio', 'estado', 'tamanio', 'imagen')
read_only_fields = ['created_at', 'updated_at']
class ContenidoSerializer(serializers.ModelSerializer):
class Meta:
model = Contenido
fields = ('id', 'habitacion_id', 'nombre', 'cantidad', 'descripcion')
read_only_fields = ['created_at', 'updated_at']
class HuespedSerializer(serializers.ModelSerializer):
class Meta:
model = Huesped
fields = ('id', 'tipo_identificacion', 'identificacion', 'nombres', 'apellidos', 'sexo', 'fecha_nacimiento', 'nacionalidad', 'region', 'telefono', 'ruc_empresa')
read_only_fields = ['created_at', 'updated_at']
class AcompananteSerializer(serializers.ModelSerializer):
class Meta:
model = Acompanante
fields = ('id', 'titular_id', 'nombres', 'apellidos', 'sexo', 'tipo_identificacion', 'identificacion', 'relacion')
read_only_fields = ['created_at', 'updated_at']
class ReservaSerializer(serializers.ModelSerializer):
class Meta:
model = Reserva
fields = ('id', 'recepcionista_id', 'titular_id', 'cantidad_dias', 'tipo_reserva', 'razon_hospedaje', 'fecha_llegada', 'peticiones', 'estado', 'habitacion_id')
read_only_fields = ['created_at', 'updated_at']
class CheckinSerializer(serializers.ModelSerializer):
class Meta:
model = Checkin
fields = ('id', 'recepcionista_id', 'reserva_id', 'fecha_entrada', 'estado', 'paxx')
read_only_fields = ['created_at', 'updated_at']
class CheckoutSerializer(serializers.ModelSerializer):
class Meta:
model = Checkout
fields = ('id', 'recepcionista_id', 'checkin_id', 'fecha_salida', 'descripcion_salida', 'tarifa')
read_only_fields = ['created_at', 'updated_at']
class RemindSerializer(serializers.ModelSerializer):
class Meta:
model = Remind
fields = ('id', 'usuario_id', 'titulo', 'descripcion')
read_only_fields = ['created_at', 'updated_at']
Las Api Views son clase que contienen las funciones de api (GET, POST, DELETE, PUT, PATCH), ya sea la petición que se haga a la url registrada se tomará un método u otro, la estructura que toman estas clases es la siguiente:
class <Model>ApiView(APIView):
permission_classes = [permissions.IsAuthenticated] // Acceso a usuarios autenticados
authentication_classes = [TokenAuthentication] // Autenticacion por token
def get(self, request, id=None, *args, **kargs):
pass
def post(self, request, *args, **kargs):
pass
def delete(self, request, id, *args, **kargs):
pass
def put(self, request, id, *args, **kargs):
pass
def patch(self, request, id, *args, **kargs):
pass
Esta aplicación fue pensada para el manejo básico del proceso de recepción de un hotel, por tal motivo no cuenta con una implementación completa o a gran escala de lo que contempla una empresa de este calibre, por lo tanto, la gran desventaja de este proyecto es el bajo panorama de validación, recolección de datos, comunicación en tiempo real, optimización de paquetes, código y alcance en cuanto a la utilidad de algunos campos de los modelos, como por ejemplo el email o telefono.
Como trabajo futuro para esta aplicación se desea lo siguiente:
- Manejo personalizado de cuentas de usuario y configuraciones adicionales de dashboard.
- Manejo de comunicación en tiempo real mediante websockets para la emisión de comunicados.
- Envío de correos a recepcionistas y clientes.
- Integración con whatsapp para uso de telefono.
- Validaciones más complejas y eficientes en cuanto al proceso de reservas.
- Optimización en la recolección de datos de huespedes.
- Integración con una aplicación móvil para las reservas virtuales y mediante cuentas de huespedes.
- Gestión de reportes más completa e informes semanales.
- Manejo de servicios adicionales y valor agregado.
- Pasarela de pagos real y facturación.
Github del proyecto (usar rama develop):
Front End: https://github.com/Derek486/hotelier-management-back.git
Back End: https://github.com/Derek486/hotelier-management-front.git
Credenciales de base de datos (local):
- Usuario: fastbook
- Contraseña: g3PqzP0up6FYm0u13pcUfQRFmkM
Credenciales de superusuario (local):
- Email: fastbook@fastbooking.com
- Contraseña: 7Cu8TL4KpFS3tturZqT5bphc
PlayList: https://www.youtube.com/playlist?list=PLgexcj5VtSG8qXptL0nDbwRmez3aBAYwi






















