{"id":26951633,"url":"https://github.com/derek486/hotelier-management-stack","last_synced_at":"2026-01-11T05:49:19.793Z","repository":{"id":186443215,"uuid":"675183111","full_name":"Derek486/hotelier-management-stack","owner":"Derek486","description":"Aplicación web con Django y Angular","archived":false,"fork":false,"pushed_at":"2025-03-25T12:50:38.000Z","size":3535,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-25T13:43:05.200Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Derek486.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-08-06T04:11:03.000Z","updated_at":"2025-03-25T12:50:42.000Z","dependencies_parsed_at":null,"dependency_job_id":"4dcc636f-e182-44a4-aa4b-3020bf8e3963","html_url":"https://github.com/Derek486/hotelier-management-stack","commit_stats":null,"previous_names":["jjcondoripinto/proyectofinal_unsa_pweb2","derek486/proyectofinal_unsa_pweb2","derek486/hotelier-management-stack"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Derek486%2Fhotelier-management-stack","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Derek486%2Fhotelier-management-stack/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Derek486%2Fhotelier-management-stack/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Derek486%2Fhotelier-management-stack/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Derek486","download_url":"https://codeload.github.com/Derek486/hotelier-management-stack/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246911469,"owners_count":20853658,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-04-03T00:16:55.036Z","updated_at":"2026-01-11T05:49:19.787Z","avatar_url":"https://github.com/Derek486.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\u003ctable\u003e\n    \u003ctheader\u003e\n        \u003ctr\u003e\n            \u003ctd style=\"width:25%;\"\u003e\u003cimg src=\"https://github.com/rescobedoq/pw2/blob/main/epis.png?raw=true\" alt=\"EPIS\" style=\"width:80%; height:auto\"/\u003e\u003c/td\u003e\n            \u003ctd\u003e\n                \u003cspan style=\"font-weight:bold;\"\u003eUNIVERSIDAD NACIONAL DE SAN AGUSTIN\u003c/span\u003e\u003cbr /\u003e\n                \u003cspan style=\"font-weight:bold;\"\u003eFACULTAD DE INGENIERÍA DE PRODUCCIÓN Y SERVICIOS\u003c/span\u003e\u003cbr /\u003e\n                \u003cspan style=\"font-weight:bold;\"\u003eDEPARTAMENTO ACADÉMICO DE INGENIERÍA DE SISTEMAS E INFORMÁTICA\u003c/span\u003e\u003cbr /\u003e\n                \u003cspan style=\"font-weight:bold;\"\u003eESCUELA PROFESIONAL DE INGENIERÍA DE SISTEMAS\u003c/span\u003e\n            \u003c/td\u003e            \n        \u003c/tr\u003e\n    \u003c/theader\u003e\n    \u003ctbody\u003e\n        \u003ctr\u003e\n        \u003ctd colspan=\"2\"\u003e\u003cspan style=\"font-weight:bold;\"\u003eProyecto web\u003c/span\u003e: Desarrollo de una aplicación web para el manejo del proceso de recepción de un hotel\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n        \u003ctd colspan=\"2\"\u003e\u003cspan style=\"font-weight:bold;\"\u003eFecha\u003c/span\u003e:  2023/08/05\u003c/td\u003e\n        \u003c/tr\u003e\n    \u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\n\u003cspan style=\"font-weight:bold;\"\u003ePROYECTO WEB\u003c/span\u003e\u003cbr /\u003e\n\u003c/div\u003e\n\n\n\u003ctable\u003e\n\u003ctheader\u003e\n\u003ctr\u003e\u003cth\u003eINFORMACIÓN BÁSICA\u003c/th\u003e\u003c/tr\u003e\n\u003c/theader\u003e\n\u003ctbody\u003e\n    \u003ctr\u003e\n        \u003ctd\u003eASIGNATURA:\u003c/td\u003e\u003ctd\u003eProgramación Web 2\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003eSEMESTRE:\u003c/td\u003e\u003ctd\u003eIII\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003eESTUDIANTE:\u003c/td\u003e\u003ctd\u003eJuan José Condori Pinto (jcondoripin@unsa.edu.pe)\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003eFECHA INICIO:\u003c/td\u003e\u003ctd\u003e01-Ago-2022\u003c/td\u003e\u003ctd\u003eFECHA FIN:\u003c/td\u003e\n        \u003ctd\u003e05-Ago-2022\u003c/td\u003e\u003ctd\u003eDURACIÓN:\u003c/td\u003e\u003ctd\u003e04 horas\u003c/td\u003e\n    \u003c/tr\u003e\n\u003c/tdbody\u003e\n\u003c/table\u003e\n\n#   WebApp con Django\n\n[![License][license]][license-file]\n[![Downloads][downloads]][releases]\n[![Last Commit][last-commit]][releases]\n\n[![Debian][Debian]][debian-site]\n[![Git][Git]][git-site]\n[![GitHub][GitHub]][github-site]\n[![Vim][Vim]][vim-site]\n[![Java][Java]][java-site]\n\n##  Tipo de Sistema\n\nSe 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.\n\n##  Requisitos del sistema\n\nRequerimientos funcionales\n\n- RQ01 : \n    - Titulo: Inicio y cierre de sesión\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.\n    - RNF: \n        - RNF01\n        - RNF03\n        - RNF04\n        - RNF05\n\n- RQ02 :\n    - Titulo: Division de trabajo por roles\n    - Descripción: El sistema debe permitir la division de responsabilidades por roles (gerente y recepcionista), designando permisos y tareas para cada uno\n    - RNF:\n        - RNF01\n        - RNF03\n        - RNF04\n\n- RQ03: \n    - Titulo: Gestion de habitaciones\n    - 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. \n    - RNF:\n        - RNF01\n        - RNF02\n        - RNF03\n        - RNF04\n\n- RQ04:\n    - Titulo: Gestion de recepcionistas\n    - 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.\n    - RNF:\n        - RNF01\n        - RNF03\n        - RNF04\n\n- RQ05:\n    - Titulo: Visualización de reportes\n    - 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.\n    - RNF: \n        - RNF02\n        - RNF04\n\n- RQ06: \n    - Titulo: Gestion de reservas\n    - 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.\n    - RNF:\n        - RNF01\n        - RNF02\n        - RNF03\n        - RNF04\n        - RNF05\n\n- RQ07:\n    - Titulo: Gestión de huespedes\n    - 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.\n    - RNF:\n        - RNF01\n        - RNF02\n        - RNF03\n        - RNF04\n    \n- RQ08:\n    - Titulo: Manejo de Checkins\n    - 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)\n    - RNF:\n        - RNF01\n        - RNF03\n        - RNF04\n\n- RQ09:\n    - Titulo: Manejo de checkouts\n    - 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\n    - RNF:\n        - RNF01\n        - RNF03\n        - RNF04\n\n- RQ10: \n    - Titulo: Manejo de recordatorios\n    - 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.\n    - RNF:\n        - RNF03\n        - RNF04\n\n\nRequerimientos no funcionales\n\n- RNF01 : \n    - Titulo: Seguridad de información\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.\n- RNF02 : \n    - Titulo: Velocidad y tiempo de respuesta\n    - Descripción: El sistema debe tener un tiempo de respuesta mínimo ante las solicitudes y manejo de eventos.\n- RNF03 : \n    - Titulo: Interfaz intuitiva\n    - Descripcion: La interfaz debe ser amigable con el usuario y accesible a todo público, sin redundancia o contenido inutilizable.\n- RNF04 : \n    - Titulo: Disponibilidad continua\n    - Descripcion: La plataforma debe estar disponible en todo momento para el usuario.\n- RNF05 : \n    - Titulo: Manejo de errores\n    - 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.\n\n\n##  Modelo de datos\n\nEl modelo de datos esta conformado por las siguientes entidades.\n\n-   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.\n\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.\n\n-   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.\n\n-   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,\n\n-   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.\n\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.\n\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'.\n\n-   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.\n\n-   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.\n\n\n##  Diccionario de datos\n\nEn 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.\n\n| Administrador | | | | | |\n| -- | -- | -- | -- | -- | -- |\n| Atributo  | Tipo  | Nulo | Unico | Predeterminado | Descripción |\n| email  | Cadena | No | Si | Ninguno | Email del usuario |\n| nombres  | Cadena | No | No | Ninguno | Nombre del usuario |\n| apellidos | Cadena | No | No | Ninguno | Apellidos del usuario |\n| dni | Numerico | No | Si | Ninguno | DNI del usuario |\n| telefono | Numerico | No | Si | Ninguno | Telefono del usuario |\n| rol | Cadena | No | No | Recepcionista | Rol del usuario |\n| turno | Cadena | Si | No | NULL | Turno del recepcionista |\n\nPara el resto de tablas se consideran los campos created_at y updated_at como Date.\n\n| Habitación | | | | | |\n| -- | -- | -- | -- | -- | -- |\n| Atributo  | Tipo  | Nulo | Unico | Predeterminado | Descripción |\n| nro_habitacion  | Numerico | No | Si | Ninguno | Nro de habitación |\n| nro_piso | Numerico | No | No | Ninguno | Nro de piso |\n| tipo_habitacion | Cadena| No | No | Ninguno | Tipo de habitación |\n| precio | Decimal | No | No | Ninguno | Precio por noche |\n| estado | Cadena | No | No | Libre | Estado de habitación |\n| tamanio | Decimal | Si | No | Ninguno | Tamaño de habitación |\n| imagen | Image | Si | No | NULL | Imagen de habitación |\n\n| Contenido | | | | | |\n| -- | -- | -- | -- | -- | -- |\n| Atributo  | Tipo  | Nulo | Unico | Predeterminado | Descripción |\n| habitacion_id | Foraneo: Habitacion | No | No | Ninguno | Llave foránea de habitación |\n| nombre | Cadena | No | No | Ninguno | Nombre de contenido |\n| cantidad | Numerico | No | No | Ninguno | Cantidad del contenido |\n| descripcion | Cadena | Si | No | NULL | Descripción del contenido |\n\n| Huesped | | | | | |\n| -- | -- | -- | -- | -- | -- |\n| Atributo  | Tipo  | Nulo | Unico | Predeterminado | Descripción |\n| tipo_identificacion | Cadena | No | No | Dni | Tipo de identificación del huesped (DNI - Carnet) |\n| identificacion | Cadena | No | Si | Ninguno | Nro de identificacion |\n| nombres | Cadena | No | No | Ninguno | Nombres de huesped |\n| apellidos | Cadena | No | No | Ninguno | Apellidos de huesped |\n| sexo | Cadena | No | No | Ninguno | Sexo de huesped |\n| fecha_nacimiento | Date | Si | No | NULL | Fecha de nacimiento del huesped |\n| nacionalidad | Cadena | No | No | Ninguno | Nacionalidad del huesped |\n| region | Cadena | No | No | Ninguno | Región dependiendo nacionalidad |\n| telefono | Numerico | Si | No | NULL | Telefono de huesped |\n| hospedado | Boolean | No | No | False | Estado de huesped (hospedado o no) |\n| ruc_empresa | Cadena | Si | No | NULL | Ruc en caso posea a una empresa |\n\n| Acompanante | | | | | |\n| -- | -- | -- | -- | -- | -- |\n| Atributo  | Tipo  | Nulo | Unico | Predeterminado | Descripción |\n| titular_id | Foraneo | No | No | Ninguno | ID del titular al que acompaña |\n| nombres | Cadena | No | No | Ninguno | Nombres de acompañante |\n| apellidos | Cadena | No | No | Ninguno | Apellidos de acompañante |\n| sexo | Cadena | No | No | Ninguno | Sexo de acompañante |\n| tipo_identificacion | Cadena | No | No | Ninguno | Tipo de identificacion (Dni - carnet) |\n| identificacion | Cadena | No | Si | Ninguno | Nro de identificación |\n| relacion | Cadena | No | No | Ninguno | Relación con titular |\n\n| Reserva | | | | | |\n| -- | -- | -- | -- | -- | -- |\n| Atributo  | Tipo  | Nulo | Unico | Predeterminado | Descripción |\n| recepcionista_id | Foraneo | No | No | Ninguno | Recepcionista quien creó la reserva |\n| titular_id | Foraneo | Si | No | NULL | ID del titular a quien pertenece |\n| cantidad_dias | Numerico | No | No | Ninguno | Cantidad de días de hospedaje |\n| tipo_reserva | Cadena | No | No | presencial | (Trabajo futuro) Medio por el cual se generó la reserva (presencial o virtual) |\n| razon_hospedaje | Cadena | No | No | Ninguno | Razón por la cual se hospeda |\n| peticiones | Texto | Si | No | NULL | Peticiones que tenga el huesped |\n| fecha_llegada | Date | No | No | Ninguno | Fecha de llegada del huesped |\n| estado | Cadena | No | No | Pendiente | Estado de reserva, sea pendiente, registrado, cancelado, pasado o registrado |\n| habitacion_id | Foraneo | No | No | Ninguno | Habitación que se reserva |\n\n| Checkin | | | | | |\n| -- | -- | -- | -- | -- | -- |\n| Atributo  | Tipo  | Nulo | Unico | Predeterminado | Descripción |\n| recepcionista_id | Foraneo | Si | No | NULL | Recepcionista quien lo generó |\n| reserva_id | Foraneo 1-1 | No | Si | Ninguno | Reserva a la cual pertenece el checkin |\n| fecha_entrada | DateTime | No | No | Ahora | Fecha de llegada para el huesped |\n| estado | Cadena | No | No | Activo | Estado de checkin (activo - inactivo) |\n| paxx | Numerico | No | No | 0 | Cantidad de acompañantes |\n\n| Checkout | | | | | |\n| -- | -- | -- | -- | -- | -- |\n| Atributo  | Tipo  | Nulo | Unico | Predeterminado | Descripción |\n| checkin_id | Foraneo 1-1 | Si | No | NULL | Recepcionista quien lo generó |\n| fecha_salida | DateTime | No | No | Ahora | Fecha de cuando se retiró el huesped |\n| descripcion_salida | Cadena | Si | No | NULL | Descripción de la salida del cliente (reseña) |\n| tarifa | Decimal | No | No | Ninguno | Tarifa por cantidad de días y precio de habitación por noche |\n\n| Remind | | | | | |\n| -- | -- | -- | -- | -- | -- |\n| Atributo  | Tipo  | Nulo | Unico | Predeterminado | Descripción |\n| titulo | Cadena | No | No | Ninguno | Titulo de recordatorio |\n| descripcion | Cadena | No | No | Ninguno | Descripción de recordatorio |\n| usuario_id | Foraneo | No | No | Ninguno | Usuario al cual pertenece |\n\n\n##  Diagrama Entidad-Relación\n\n![alt](./img/diagrama_ed.png)\n\n##  Administración con Django\n\nSe 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.\n    \n### Creación de proyecto\n\nLa creación de un proyecto en django se realiza mediante el siguiente comando:\n\n\n`django-admin startproject FastBooking .`\n\nPara la aplicación presentada, el proyecto se llama 'FastBooking' y consta de una aplicación 'Api'\n\n`django-admin startapp Api`\n\nLa aplicación de django no consta de una interfaz personalizada, sino la que nos proporciona por defecto el panel de administrador.\n\n### Creación de modelos\n\nLos 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:\n\n```\nclass CustomUserManager(UserManager):\n    def _create_user(self, email,   password, **extra_fields):\n        if not email:\n            raise ValueError(\"Debes proporcionar un email valido\")\n        email = self.normalize_email(email)\n        user = self.model(email=email,**extra_fields)\n        user.set_password(password)        user.save(using=self.db)\n\n        return user\n\n    def create_user(self, email=None, password=None, **extra_fields):\n        extra_fields.setdefault('is_staff', False)\n        extra_fields.setdefault('is_superuser', False)\n        return self._create_user(email, password, **extra_fields)\n    \n    def create_superuser(self, email=None, password=None, **extra_fields):\n        extra_fields.setdefault('is_staff', True)\n        extra_fields.setdefault('is_superuser', True)\n        extra_fields.setdefault('rol', 'gerente')\n        return self._create_user(email, password, **extra_fields)\n```\n\nLa clase presentada es la base de un usuario autenticado, en ella se definen los métodos básicos de creación\n\n```\nclass User(AbstractBaseUser, PermissionsMixin):\n    roles = [\n        ('recepcionista', 'recepcionista'),\n        ('gerente', 'gerente')\n    ]\n    turnos = [\n        ('mañana', 'mañana'),\n        ('tarde', 'tarde'),\n        ('noche', 'noche'),\n        ('finesSemana', 'finesSemana'),\n    ]\n\n    email = models.EmailField(blank=True, default='', unique=True)\n    nombres = models.CharField(max_length=255)\n    apellidos = models.CharField(max_length=255)\n    dni = models.PositiveIntegerField(unique=True)\n    telefono = models.PositiveIntegerField(unique=True)\n    rol = models.CharField(max_length=15, choices=roles, default='recepcionista')\n    turno = models.CharField(max_length=15, choices=turnos)\n\n    is_active = models.BooleanField(default=True)\n    is_superuser = models.BooleanField(default=False)\n    is_staff = models.BooleanField(default=False)\n\n    date_joined = models.DateTimeField(default=timezone.now)\n    last_login = models.DateTimeField(blank=True, null=True)\n\n    objects = CustomUserManager()\n\n    USERNAME_FIELD = 'email'\n    EMAIL_FIELD = 'email'\n    REQUIRED_FIELDS = ['nombres', 'apellidos', 'dni', 'telefono']\n    \n    class Meta:\n        db_table = 'administradores'\n        verbose_name = 'administrador'\n        verbose_name_plural = 'administradores'\n    \n    def get_full_name(self):\n        return self.nombres\n    \n    def get_short_name(self):\n        return self.nombres or self.email.split('@')[0]\n```\n\nLa 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\n\n    \n#### Clase habitacion:\n\n```\nclass Habitacion(models.Model):\n\n    estados = [\n        ('Libre', 'Libre'),\n        ('Ocupado', 'Ocupado'),\n        ('Reservado', 'Reservado'),\n        ('Limpieza', 'Limpieza'),\n        ('No operativo', 'NoOperativo'),\n    ]\n\n    nro_habitacion = models.PositiveIntegerField(unique=True, null=False)\n    nro_piso = models.PositiveSmallIntegerField(null=False)\n    tipo_habitacion = models.CharField(null=False, max_length=30)\n    precio = models.DecimalField(max_digits=6, decimal_places=2, null=False)\n    estado = models.CharField(choices=estados, default='Libre', max_length=12)\n    tamanio = models.DecimalField(max_digits=4, decimal_places=2)\n    imagen = models.ImageField(upload_to='habitaciones', null=True)\n\n    created_at = models.DateTimeField(auto_now_add=True, null=False)\n    updatedat = models.DateTimeField(auto_now=True)\n```\n\n#### Clase contenido:\n\n```\nclass Contenido(models.Model):\n    habitacion = models.ForeignKey(Habitacion, on_delete=models.CASCADE, null=False)\n    nombre = models.CharField(max_length=100, null=False)\n    cantidad = models.PositiveIntegerField(default=1, null=False)\n    descripcion = models.CharField(max_length=100, null=True)\n\n    created_at = models.DateTimeField(auto_now_add=True, null=False)\n    updated_at = models.DateTimeField(auto_now=True)\n```\n\n#### Clase Huesped:\n\n```\nclass Huesped(models.Model):\n    tipos_identificacion = [\n        ('Dni', 'Dni'),\n        ('Carnet_Extranjeria', \"Carnet_Extranjeria\")\n    ]\n\n    tipo_identificacion = models.CharField(max_length=20, choices=tipos_identificacion, default='Dni')\n    identificacion = models.CharField(max_length=20, null=False, unique=True)\n\n    nombres = models.CharField(max_length=50, null=False)\n    apellidos = models.CharField(max_length=50, null=False)\n    sexo = models.CharField(choices=[('Masculino', 'masculino'), ('Femenino', 'femenino')], null=False, max_length=9)\n    fecha_nacimiento = models.DateField(auto_created=False)\n\n    nacionalidad = models.CharField(max_length=20, null=False)\n    region = models.CharField(max_length=30)\n    telefono = models.PositiveIntegerField()\n\n    hospedado = models.BooleanField(default=False)\n\n    created_at = models.DateTimeField(auto_now_add=True, null=False)\n    updated_at = models.DateTimeField(auto_now=True)\n\n    # Opcional, si el huesped viaja por motivos de negocio\n    ruc_empresa = models.CharField(null=True, max_length=11, blank=True)\n```\n\n#### Clase Acompanante:\n\n```\nclass Acompanante(models.Model):\n    titular = models.ForeignKey(Huesped, on_delete=models.CASCADE)\n    nombres = models.CharField(max_length=50)\n    apellidos = models.CharField(max_length=50)\n    sexo = models.CharField(choices=[('Masculino', 'masculino'), ('Femenino', 'femenino')], null=False, max_length=9)\n\n    tipo_identificacion = models.CharField(max_length=20, null=True)\n    identificacion = models.CharField(max_length=20, null=True, unique=True)\n\n    relacion = models.CharField(max_length=50, help_text=\"Relación con el titular\")\n\n    created_at = models.DateTimeField(auto_now_add=True, null=False)\n    updated_at = models.DateTimeField(auto_now=True)\n```\n\n#### Clase Reserva:\n\n```\nclass Reserva(models.Model):\n\n    estados = [\n        ('Pendiente', 'pendiente'), # Aún ho hay checkin\n        ('Cancelado', 'cancelado'), \n        ('Pasado', 'pasado'),  # Se realizó el checkout\n        ('Registrado', 'Registrado') # Cuando se realizó el checkin\n    ]\n\n    # Para saber cuál recepcionista lo realizó\n    recepcionista = models.ForeignKey(User, on_delete=models.SET_NULL, name='recepcionista', null=True)\n\n    titular = models.ForeignKey(Huesped, on_delete=models.CASCADE, null=False)\n    cantidad_dias = models.PositiveIntegerField(null=False)\n    tipo_reserva = models.CharField(choices=[('Presencial', 'presencial'), ('Virtual', 'virtual')], default='presencial', max_length=15)\n    razon_hospedaje = models.CharField(max_length=50)\n    peticiones = models.TextField(max_length=100, blank=True, null=True)\n    fecha_llegada = models.DateField(null=False)\n    estado = models.CharField(choices=estados, default='Pendiente', max_length=10)\n\n    habitacion = models.ForeignKey(Habitacion, on_delete=models.CASCADE, null=False)\n\n    created_at = models.DateTimeField(auto_now_add=True, null=False)\n    updated_at = models.DateTimeField(auto_now=True)\n```\n\n#### Clase Checkin:\n\n```\nclass Checkin(models.Model):\n\n    estados = [\n        ('Activo', 'activo'), # Checkin vigente\n        ('Inactivo', 'inactivo') # Checkout realizado\n    ]\n    \n    # Para saber cuál recepcionista lo realizó\n    recepcionista = models.ForeignKey(User, on_delete=models.SET_NULL, name='recepcionista', null=True)\n\n    reserva = models.OneToOneField(Reserva,on_delete=models.CASCADE)\n    fecha_entrada = models.DateTimeField(auto_now_add=True, null=False)\n    estado = models.CharField(choices=estados, default='Activo', max_length=8)\n\n    paxx = models.PositiveIntegerField(default=0) # Número de acompañantes\n\n    created_at = models.DateTimeField(auto_now_add=True, null=False)\n    updated_at = models.DateTimeField(auto_now=True)\n```\n\n#### Clase Checkout:\n\n```\nclass Checkout(models.Model):\n\n    # Para saber cuál recepcionista lo realizó\n    recepcionista = models.ForeignKey(User, on_delete=models.SET_NULL, name='recepcionista', null=True)\n\n    checkin = models.OneToOneField(Checkin, on_delete=models.CASCADE)\n    fecha_salida = models.DateTimeField(auto_now_add=True, null=False)\n    descripcion_salida = models.CharField(blank=True, null=True, max_length=500)\n    tarifa = models.DecimalField(decimal_places=2, max_digits=7, null=False)\n\n    created_at = models.DateTimeField(auto_now_add=True, null=False)\n    updated_at = models.DateTimeField(auto_now=True)\n```\n\n#### Clase Remind (recordatorio):\n\n```\nclass Remind(models.Model):\n\n    titulo = models.CharField(max_length=50, null=False)\n    descripcion = models.CharField(max_length=255, null=False)\n    usuario = models.ForeignKey(User, on_delete=models.CASCADE, null=False)\n\n    created_at = models.DateTimeField(auto_now_add=True)\n    updated_at = models.DateTimeField(auto_now=True)\n```\n\nNote 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.\n\nPara migrar las tablas a la base de datos es necesario primero crear las migraciones y ejecutarlas a través de los comandos:\n\n`python manage.py makemigrations`\n`python manage.py migrate`\n\nLuego de ello debemos registrar un superusuario (gerente) para que este pueda generar los recepcionistas y gestionar sus cuentas\n\n`python manage.py createsuperuser`\n\nSi los modelos se colocaron de forma correcta, para la creación del superusuario debería pedir email, nombres, apellidos, telefono, dni y contraseña.\n\nUna 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.\n\n```\nfrom django.contrib import admin\nfrom .models import User\n\nadmin.site.register(User)\n```\n\nUna vez colocado, en el panel de administrador nos debería de aparecer algo parecido a esto:\n\n![alt](./img/admin.png)\n\n\n##  Vistas de front end\n\nEl front end fue desarrollado en Angular v16 mediante el uso de componentes, rutas, guards, servicios e interfaces Typescript.\n\n![alt](./img/login.png)\n    \nEl 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:\n\n![alt](./img/dashboard_gerente.png)\n\nY otra para el recepcionista:\n\n![alt](./img/dashboard_recep.png)\n\nAmbas vistas cuentan con el cambio de tema a oscuro, contracción de sidebar y generación de recordatorios.\n\n### Funcionalidad para gerente\n\n#### Habitaciones\n\nEl gerente tiene la potestad de generar habitacioens y los recepcionistas que pertenecerán a su hotel:\n\n![alt](./img/habitaciones.png)\n\nComo se logra observar, se pueden generar, eliminar y también buscar habitaciones según un campo especificado en el header de la tabla\n\n![alt](./img/habitacion_show.png)\n\nEsta 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.\n\n#### Recepcionistas\n\nLo más importante para el gerente es la generación de recepcionistas para su hotel, estos se contemplan en la siguiente vista:\n\n![alt](./img/recepcionistas.png)\n\nSe pueden generar y eliminar, también buscar por campo\n\n![alt](./img/recepcionista_show.png)\n\nEsta vista sirve para la modificación de datos, también para el registro de recepcionistas.\n\n#### Reportes\n\nEn 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.\n\n![alt](./img/reportes.png)\n\n### Funcionalidad para recepcionistas\n\n#### Huespedes:\n\n![alt](./img/huespedes.png)\n\nComo 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.\n\n![alt](./img/huesped_show.png)\n\nEn 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).\n\n#### Habitaciones:\n\n![alt](./img/habitaciones_recep.png)\n\nLa vista de habitaciones para recepcionista solo le permite visualizar el listado y poder modificar el estado en el que se encuentran.\n\n#### Reservas:\n\n![alt](./img/reservas.png)\n\nLa vista de reservas le permite a lrecepcionista visualizar un listado, genear, editar, cancelar, eliminar y hacer un checkin de reservas.\n\n![alt](./img/reserva_show.png)\n\nEn 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.\n\n![alt](./img/reserva_create.png)\n\nCuando 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.\n\n#### Checkins:\n\n![alt](./img/checkins.png)\n\nLa 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\n\n#### Checkouts:\n\n![alt](./img/checkouts.png)\n\nEn 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 \n\n#### Recordatorios:\n\n![alt](./img/reminds.png)\n\nLa funcionalidad de recordatorios puede ser utilizada tanto por el gerente como el recepcionista, ambos generan un recordatorio en caso requieran.\n\n![alt](./img/remind.png)\n\n### Estructura de FrontEnd:\n\n![alt](./img/estructura_front.png)\n\nEl 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\n\n![alt](./img/estructura_views.png)\n\nEsta 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'\n\n### Comunicación con backend:\n\nLa 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:\n\n```\nexport class LoginService extends ApiService{\n\n  private apiUrl : string = `${this.HOST_API}${this.API_AUTH}`;\n  private apiUrlLogout : string = `${this.HOST_API}${this.API_BACK}auth/`;\n\n\n  login(user : User) {\n    return this.httpClient.post(`${this.apiUrl}`, user)\n  }\n\n  logout() {\n    return this.httpClient.post(this.apiUrlLogout, null, { headers: this.getHeaders() })\n  }\n\n}\n```\n\nEste servicio de login hereda de una super clase ApiService, en esta misma se encuentra la url del backend y los headers de autenticación:\n\n```\nexport abstract class ApiService { // Servicio global para apis\n\n   HOST_API: string = 'http://localhost:8000'\n   API_AUTH: string = '/api-token-auth/'\n   API_BACK: string = '/api/v1/'\n\n  protected HEADER: HttpHeaders = new HttpHeaders();\n\n  constructor(protected httpClient : HttpClient, private storage : StorageInfoService) { }\n\n  getHeaders() : HttpHeaders {\n    return this.HEADER.append('Authorization', `Token ${this.storage.getKeyToken()}`);\n  }\n\n}\n```\n\nEstas 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:\n\n```\n  constructor(private service : LoginService, private storage : StorageInfoService, private route : Router ) { }\n\n  login() {\n    if (!this.formData.invalid) {\n\n      this.error = '';\n      this.errorEmail = false;\n      this.errorPassword= false;\n\n      this.service.login(this.formData.value as User).subscribe({\n        next: ((res : any) =\u003e { // Response ok\n          this.storage.setInfo(res);\n          this.route.navigate([`dashboard/${this.storage.getRol()}/`]); // Redireccion a ruta segun rol\n        }),\n        error: (err : any) =\u003e { // Error\n          // Mensajes de error en vista\n          this.error = 'Credenciales incorrectas';\n          this.errorEmail = true;\n          this.errorPassword= true;\n         }\n      });\n    }\n  }\n```\n\n\n##  CRUD - Core Business - Clientes finales\n\nEl 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:\n\n1. El recepcionista inicia sesión en la aplicación.\n2. El cliente llega al hotel y pide una reserva.\n3. El recepcionista registra los datos del huesped y de su reserva, visualiza las habitaciones disponibles.\n4. Se genera la reserva y antes de realizar el checkin, se deben de registrar los acompañantes.\n5. Una vez generados los acompañantes y el huesped haya llegado para su hospedaje, se realiza el checkin.\n6. Pasado el tiempo de estadía, el huesped se retira, a esto último se le conoce como checkout.\n\nLa estructura de las plantillas para el front end de la aplicación se explicó anteriormente, así como la creación de los modelos.\n\n##  Servicios mediante una API RESTful\n    \nSe 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.\n\n### Serializers:\n\nFue creado un serializer por cada modelo, en él se incluyen los campos a serializar, aquellos de lectura y el modelo:\n\n#### User Serializer:\n\n```\nclass UserSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = User\n        fields = ('id', 'nombres', 'apellidos',  'dni', 'telefono', 'turno', 'email')\n        read_only_fields = ['rol', 'created_at', 'updated_at']\n\n    def create_user(self, validated_data):\n        User.objects.create_user(**validated_data)\n\n    def update_user_password(self, validated_data, id):\n        if 'password' in validated_data:\n            user = User.objects.get(pk=id)\n            user.set_password(validated_data['password'])\n            user.save()\n```\n\n#### Habitación Serializer:\n\n```\nclass HabitacionSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Habitacion\n        fields = ('id', 'nro_habitacion', 'nro_piso', 'tipo_habitacion', 'precio', 'estado', 'tamanio', 'imagen')\n        read_only_fields = ['created_at', 'updated_at']\n```\n\n#### Contenido Serializer:\n\n```\nclass ContenidoSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Contenido\n        fields = ('id', 'habitacion_id', 'nombre', 'cantidad', 'descripcion')\n        read_only_fields = ['created_at', 'updated_at']\n```\n\n#### Huesped Serializer:\n\n```\nclass HuespedSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Huesped\n        fields = ('id', 'tipo_identificacion', 'identificacion', 'nombres', 'apellidos', 'sexo', 'fecha_nacimiento', 'nacionalidad', 'region', 'telefono', 'ruc_empresa')\n        read_only_fields = ['created_at', 'updated_at']\n```\n\n#### Acompanante Serializer:\n\n```\nclass AcompananteSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Acompanante\n        fields = ('id', 'titular_id', 'nombres', 'apellidos', 'sexo', 'tipo_identificacion', 'identificacion', 'relacion')\n        read_only_fields = ['created_at', 'updated_at']\n```\n\n#### Reserva Serializer:\n\n```\nclass ReservaSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Reserva\n        fields = ('id', 'recepcionista_id', 'titular_id', 'cantidad_dias', 'tipo_reserva', 'razon_hospedaje', 'fecha_llegada', 'peticiones', 'estado', 'habitacion_id')\n        read_only_fields = ['created_at', 'updated_at']\n```\n\n#### Checkin Serializer:\n\n```\nclass CheckinSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Checkin\n        fields = ('id', 'recepcionista_id', 'reserva_id', 'fecha_entrada', 'estado', 'paxx')\n        read_only_fields = ['created_at', 'updated_at']\n```\n\n#### Checkout Serializer:\n\n```\nclass CheckoutSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Checkout\n        fields = ('id', 'recepcionista_id', 'checkin_id', 'fecha_salida', 'descripcion_salida', 'tarifa')\n        read_only_fields = ['created_at', 'updated_at']\n```\n\n#### Remind Serializer:\n\n```\nclass RemindSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Remind\n        fields = ('id', 'usuario_id', 'titulo', 'descripcion')\n        read_only_fields = ['created_at', 'updated_at']\n```\n\n### ApiViews\n\nLas 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:\n\n```\nclass \u003cModel\u003eApiView(APIView):\n    permission_classes = [permissions.IsAuthenticated] // Acceso a usuarios autenticados\n    authentication_classes = [TokenAuthentication] // Autenticacion por token\n\n    def get(self, request, id=None, *args, **kargs):\n        pass\n\n    def post(self, request, *args, **kargs):\n        pass\n\n    def delete(self, request, id, *args, **kargs):\n        pass\n\n    def put(self, request, id, *args, **kargs):\n        pass\n\n    def patch(self, request, id, *args, **kargs):\n        pass\n``` \n\n\n## Conclusión y trabajo futuro\n\nEsta 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.\n\nComo trabajo futuro para esta aplicación se desea lo siguiente:\n\n- Manejo personalizado de cuentas de usuario y configuraciones adicionales de dashboard.\n- Manejo de comunicación en tiempo real mediante websockets para la emisión de comunicados.\n- Envío de correos a recepcionistas y clientes.\n- Integración con whatsapp para uso de telefono.\n- Validaciones más complejas y eficientes en cuanto al proceso de reservas.\n- Optimización en la recolección de datos de huespedes.\n- Integración con una aplicación móvil para las reservas virtuales y mediante cuentas de huespedes.\n- Gestión de reportes más completa e informes semanales.\n- Manejo de servicios adicionales y valor agregado.\n- Pasarela de pagos real y facturación.\n\n\nGithub del proyecto (usar rama develop):\n\nFront End: https://github.com/Derek486/hotelier-management-back.git\n\nBack End: https://github.com/Derek486/hotelier-management-front.git\n\nCredenciales de base de datos (local):\n\n- Usuario: fastbook\n- Contraseña: g3PqzP0up6FYm0u13pcUfQRFmkM\n\nCredenciales de superusuario (local):\n\n- Email: fastbook@fastbooking.com\n- Contraseña: 7Cu8TL4KpFS3tturZqT5bphc\n\nPlayList: https://www.youtube.com/playlist?list=PLgexcj5VtSG8qXptL0nDbwRmez3aBAYwi\n\n## REFERENCIAS\n\n-   https://www.djangoproject.com/\n-   https://angular.io/\n\n#\n\n[license]: https://img.shields.io/github/license/rescobedoq/pw2?label=rescobedoq\n[license-file]: https://github.com/rescobedoq/pw2/blob/main/LICENSE\n\n[downloads]: https://img.shields.io/github/downloads/rescobedoq/pw2/total?label=Downloads\n[releases]: https://github.com/rescobedoq/pw2/releases/\n\n[last-commit]: https://img.shields.io/github/last-commit/rescobedoq/pw2?label=Last%20Commit\n\n[Debian]: https://img.shields.io/badge/Debian-D70A53?style=for-the-badge\u0026logo=debian\u0026logoColor=white\n[debian-site]: https://www.debian.org/index.es.html\n\n[Git]: https://img.shields.io/badge/git-%23F05033.svg?style=for-the-badge\u0026logo=git\u0026logoColor=white\n[git-site]: https://git-scm.com/\n\n[GitHub]: https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge\u0026logo=github\u0026logoColor=white\n[github-site]: https://github.com/\n\n[Vim]: https://img.shields.io/badge/VIM-%2311AB00.svg?style=for-the-badge\u0026logo=vim\u0026logoColor=white\n[vim-site]: https://www.vim.org/\n\n[Java]: https://img.shields.io/badge/java-%23ED8B00.svg?style=for-the-badge\u0026logo=java\u0026logoColor=white\n[java-site]: https://docs.oracle.com/javase/tutorial/\n\n\n[![Debian][Debian]][debian-site]\n[![Git][Git]][git-site]\n[![GitHub][GitHub]][github-site]\n[![Vim][Vim]][vim-site]\n[![Java][Java]][java-site]\n\n\n[![License][license]][license-file]\n[![Downloads][downloads]][releases]\n[![Last Commit][last-commit]][releases]\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fderek486%2Fhotelier-management-stack","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fderek486%2Fhotelier-management-stack","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fderek486%2Fhotelier-management-stack/lists"}