Compare commits

...

17 Commits

Author SHA1 Message Date
Lucho 1cda04fd08 Código fuente limpio 2026-06-24 18:41:05 -03:00
Lucho 897581a97d Documentación, scripts auxiliares y limpieza de archivos eliminados 2026-06-24 16:32:01 -03:00
Lucho 4d29f6bb49 Archivos de configuración global, dependencias y assets base 2026-06-24 16:30:14 -03:00
Lucho 9474236226 Rutas del sistema y archivos de configuración 2026-06-24 16:29:04 -03:00
Lucho c81120f2e3 Estructura de base de datos, migraciones, factories y seeders 2026-06-24 16:28:01 -03:00
Lucho 317d85b5c3 Actualización de modelos, controladores y middleware 2026-06-24 16:25:36 -03:00
Lucho ff2fa9b70f Resto de las vistas y plantillas del sistema 2026-06-24 16:21:44 -03:00
Lucho 7a622f469d Vistas del panel de profesionales 2026-06-24 16:20:36 -03:00
Lucho 12a5df4ee0 Vistas del panel de clientes 2026-06-24 16:19:32 -03:00
Lucho 2a27c84d24 Vista principal welcome 2026-06-24 16:14:44 -03:00
Lucho 25f4b73b68 Comence con las vistas. El modelo de agenda está practicamente terminado 2026-03-25 13:51:02 -03:00
Lucho 6c2c300d6e Se terminó de crear los controladores con los métodos CRUD básicos. Lo siguiente es empezar a programar los métodos especificos de cada controlador 2026-03-21 09:53:45 -03:00
Lucho 7b7d81d5d0 se terminaron los modelos, faltan controlarlos con la IA 2026-03-16 15:14:17 -03:00
Lucho a311dedf89 Sigo trabajando en los modelos y seeders. (Se corrigió un error de secuencia en la creacion de migraciosnes. logsseguridades debe crearse despues de personas) 2026-03-11 14:16:25 -03:00
Lucho 6c9d79115b Se comenzó a programar seeders y modelos (las migraciones ya están terminadas) 2026-03-10 16:41:34 -03:00
Lucho 55fc8aa0a8 Base de datos creada. Diagrama de clases agregado. Documentación de diagrama de clases y DER agregados. 2026-03-06 11:39:11 -03:00
Lucho 17dee644bd MIGRACIONES: 27 tablas creadas en total (aún no se ejecutó el comando de migraciones!) 2026-03-05 17:00:40 -03:00
291 changed files with 33246 additions and 335 deletions
+16 -3
View File
@@ -4,9 +4,9 @@ APP_KEY=
APP_DEBUG=true APP_DEBUG=true
APP_URL=http://localhost APP_URL=http://localhost
APP_LOCALE=en APP_LOCALE=es
APP_FALLBACK_LOCALE=en APP_FALLBACK_LOCALE=es
APP_FAKER_LOCALE=en_US APP_FAKER_LOCALE=es_AR
APP_MAINTENANCE_DRIVER=file APP_MAINTENANCE_DRIVER=file
# APP_MAINTENANCE_STORE=database # APP_MAINTENANCE_STORE=database
@@ -62,4 +62,17 @@ AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET= AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false AWS_USE_PATH_STYLE_ENDPOINT=false
BACKUP_DESTINATION_DISK=local
BACKUP_MAIL_TO=admin@example.com
BACKUP_ARCHIVE_PASSWORD=
BACKUP_INCLUDE_ENV=false
BACKUP_MAX_AGE_DAYS=7
BACKUP_MAX_STORAGE_MB=5000
BACKUP_KEEP_ALL_DAYS=7
BACKUP_KEEP_DAILY_DAYS=16
BACKUP_KEEP_WEEKLY_WEEKS=8
BACKUP_KEEP_MONTHLY_MONTHS=4
BACKUP_KEEP_YEARLY_YEARS=2
BACKUP_MAX_STORAGE_LIMIT_MB=5000
VITE_APP_NAME="${APP_NAME}" VITE_APP_NAME="${APP_NAME}"
View File
+455
View File
@@ -0,0 +1,455 @@
@startuml
' Configuración visual
skinparam classAttributeIconSize 0
skinparam linetype ortho
interface INotificadorTurnos <<interface>>
{
+ NotificarCancelacionTurno(mensaje:string, turno : Turno, destinatario : string)
+ NotificarAsignacionTurno(mensaje:string, turno : Turno, destinatario : string)
+ NotificarRecordatorio(mensaje:string, turno : Turno, destinatario : string)
}
interface INotificadorCrearCliente <<interface>>
{
+ NotificarClienteCreado (cliente : Cliente, mensaje : string, destinatario : string)
}
interface INotificadorCrearProfesional <<interface>>
{
+ NotificarProfesionalCreado(profesional : Profesional, mensaje : string, destinatario : string)
}
interface INotificadorUsuario <<interface>> {
+ NotificarRecuperacionCredenciales(destinatario: string, link : string, token : string)
}
class ServicioMail {
- mensaje : string
- T : Turno //dentro de Turno, tengo el correo del destinatario
+ ServicioMail(T : Turno, mensaje: string)
+ ServicioMail(mensaje: string)
+ NotificarCancelacionTurno(mensaje:string, turno : Turno, destinatario : string)
+ NotificarAsignacionTurno(mensaje:string, turno : Turno, destinatario : string)
+ NotificarRecordatorio(mensaje:string, turno : Turno, destinatario : string)
+ NotificarClienteCreado (cliente : Cliente, mensaje : string, destinatario : string)
+ NotificarProfesionalCreado(profesional : Profesional, mensaje : string, destinatario : string)
+ NotificarRecuperacionCredenciales(token : string, destinatario: string)
}
class AuthController {
//Recuperacion
+ RecuperarProf(ROL : string, usuario : string, email :string, notificador : INotificadorUsuario) : void
+ RecuperarCli(usuario : string, email :string, notificador : INotificadorUsuario) : void
+ RecuperarAdmin(DNI : string, correo : string, CUIL : string) //En caso de que el admin se olvide su usuario
- GenerarToken(ROL : string, usuario : string) : string //genera y guarda en la base de datos
- ExisteUsuario(ROL : string, usuario : string) : bool
- CorreoPertenece(ROL : string, ID_Credencial : integer) : bool
- ExisteAdmin(DNI, correo, CUIL) : bool
//Login
+ LoginCliente(string, string) : bool
+ LoginProfesional(string, string) : bool
}
package "Gestión de Usuarios" {
class Persona {
- Per_DNI: integer
- Per_Nombre: string
- Per_Apellido: string
- Per_CUIL: string
- Per_FechaNac: date
+ Persona(integer, string, string, string, date) : void
- Existe(Per_DNI) : bool
+ GuardarPersona(p:Persona, telefonos : array, foto_nombre:string, foto_mime_type: string, foto_tamanio_bytes: string, foto_extension: string) : bool
}
class Telefono {
- Tel_ID: integer
- Tel_Numero: bigint
+ GuardarTelefono(DNI : integer, Telefono : integer) : void
- Existe() : bool
+ GetTelefonos (DNI: integer) : array
}
class Administrador {
- Admin_Correo: string
- Admin_DNI: integer
+ Administrador(correo: string, dni: integer) : void
+ Recuperacion(correo : string, dni : integer, matricula : integer) : void
+ EditarAdmin(nombre: string, apellido: string, correo: string, correoViejo: string, dni: integer) : bool
+ Login(string, string) : bool
+ CerrarSesion() : void
+ VerLogs() : array
}
class Cliente {
- Cli_DNI: integer
- Cli_Correo: string
+ Cliente(correo: string, dni: integer, foto_nombre:string, foto_mime_type: string, foto_tamanio_bytes: string, foto_extension: string) : bool
+ Login(string, string) : bool
+ EnviarFormulario(NombreyApellido:string, correo:string, celular:string, matriculaProfPref: string, dniProfPref: integer) : bool //se crea una instancia de formulario
+ GuardarCliente(Cliente) : bool
+ EditarCliente(integer, string, string, string, date, string) : bool
+ MostrarClientes() : array
+ CambiarEstado(Prof_DNI: integer, Prof_Matricula) : void
+ CerrarSesion() : void
}
class Profesional {
- Profesion_ID: integer
- Profesional_Matricula: integer
- Prof_Correo: string
+ Profesional(matricula : integer, correo : string)
+ Profesional(id : integer, matricula : integer, correo : string)
+ Login(string, string) : bool
+ CerrarSesion() : void
+ Recuperacion(correo : string, dni : integer, matricula : integer)
+ EditarProfesional(Profesion_DNI:integer, Profesion_Matricula : integer, Persona, Foto) : bool
+ AddProfesional(Profesion_DNI:integer, Profesion_Matricula : integer, Persona, Foto) : bool
+ VerProfesionales() : array
+ ConfigurarAgenda() : void
+ DarDeBaja() : void
+ CrearTurnoManual(F : Formulario, FechaHora : datetime) : Turno//crea una instancia de turno, llamo a GuardarTurno() de Agenda
+ CrearTurnoAutomatico(F : Formulario) : Turno //crea una instancia de turno, llamo a GuardarTurno() de Agenda
}
class Credencial {
- Cred_Usuario: string
- Cred_Contra: string
- ROL: string
+ Credencial(userForm : string, passForm : string, ROL : string) //verificar si no existe ese usuario
+ LoginProfesional(userForm : string, passForm : string) : bool //verificar estado del profesional (si está de baja, no debe poder iniciar sesion)
+ LoginCliente(userForm : string, passForm : string) : bool //Verificar si está de baja
- CredencialCorrecta(string, string) : bool
+ GuardarCredencial(Credencial) : integer //Devuelve el ID generado por la BD
+ CambiarContrasenia(ID_Credencial : integer, pass : string, ROL : string) : bool
+ GuardarToken(token : string, HoraFecha : datetime) : void
+ ValidarToken(ROL : string, tokenForm: string, ID_Credencial : integer) : bool
+ ExisteUsuario(ROL : string, usuario : string) : bool
}
class EstadoProfesional {
- Estado_ID: integer
- Estado_Descripcion: string
+ BajaProfesional(Prof_DNI: string, Prof_Matricula: string)
+ ReactivarProfesional(Prof_DNI: string, Prof_Matricula: string)
}
}
package "Gestión de Turnos y Agenda" {
class Turno {
- Turno_ID: integer
- Turno_Inicio: datetime
- Turno_Correo: string //En caso de que no sea Cliente, sino un visitante
- Turno_NombreCompleto: string //En caso de que no sea Cliente, sino un visitante
+ Turno(Turno_Inicio : datetime, Turno_Correo : string, Turno_NombreCompleto : string) //Si es cliente, correo y nombre son NULL
+ CancelarTurno(ID_Turno : integer, INotificador : INotificadorTurnos)
+ VerTurnosCliente(Cliente_DNI: integer)
+ VerTurnosProfesional(id_Profesion : integer, Prof_Matricula : integer)
}
class EstadoTurno {
- EstadoTurno_ID: integer
- EstadoTurno_Descripcion: string
+ EstadoTurno()
+ CambiarEstado(descripcion : string) : void
}
class Agenda {
- Agenda_ID: integer
- Estado: string
- DuracionTurno: integer
+ Agenda(DuracionTurno: integer, prof: Profesional)
+ VerAgenda(): Agenda
+ GuardarTurno(T: Turno, INotificador : INotificadorTurnos) : bool
- Disponible(date) : bool //para asignar manualmente
- VerificarDisponibilidad(f: Formulario) : datetime //Para asignacion automatica
+ crearDiaDeAtencion(diaid, horarioComienzo, horarioFin, tipo) : void
+ GuardarModoVacaciones(ModoVacaciones) : void
+ GuardarFeriado(Feriado) : void
}
class DiaDeAtencion {
- Dia_ID: integer
+ DiaDeAtencion(ID_Dia : integer, comienzo : integer, fin : integer, tipo : string) //tipo = 'AM', 'PM', 'INDISTINTO'
+ GuardarHorarioAtencion(HorarioAtencion) : void
+ GuardarHorarioReceso(HorarioReceso) : void
+ BuscarID() : integer
+ EliminarHorarioAtencion(ID : integer) : void
+ EliminarHorarioReceso(ID : integer) : void
}
class HorarioAtencion {
- Horario_ID: integer
- Horario_Comienzo: time
- Horario_Final : time
+ HorarioAtencion(Inicio : time, Final : time) : HorarioAtencion
- BuscarIDHorarioAtencion(HorarioAtencion) : integer
+ EliminarHorarioAtencion(HorarioAtencion) : void
}
class HorarioReceso {
- Receso_ID: integer
- Receso_Comienzo: time
- Receso_Fin: time
+ HorarioReceso(Inicio : time, Final : time) : HorarioReceso
- BuscarIDHorarioReceso(HorarioReceso) : integer
+ EliminarHorarioReceso(HorarioReceso) : void
}
class ModoVacaciones {
- Vacaciones_ID: integer
- Vacaciones_Inicio: date
- Vacaciones_Fin: date
+ ModoVacaciones(Inicio : time, Final : time)
- BuscarIDModoVacaciones(ModoVacaciones) : integer
+ EliminarModoVacaciones(ModoVacaciones) : void
- ValidarFecha(inicio, fin) : bool
}
class Feriados {
-Feriado_ID: integer
-Feriado_fecha : date
-Feriado_Descripcion : string
+ Feriado(fecha : date, descripcion : string)
- BuscarIDFeriado(Feriado) : integer
+ EliminarFeriado(Feriado) : void
}
}
class Profesion {
- Profesion_ID: integer
- Profesion_Titulo: string
- VisibleEnFormulario: bool
- Existe(titulo:string) : bool
+ AddProfesion(Titulo:string)
+ DesactivarProfesion(Profesion_ID: integer) //VisibleEnFormulario = false
}
class Servicio {
- Ser_ID: integer
- Ser_Descripcion: string
- Ser_Titulo: string
- Estado: string
- Existe(titulo : string) : bool //se llama antes de agregar un servicio
- BuscarID(titulo : string) : integer
+ AddServicio(titulo:string, descripcion:string, foto_nombre:string, foto_mime_type: string, foto_tamanio_bytes: string, foto_extension: string, Id_profesion: integer)
+ EditarServicio(Ser_ID:integer, titulo:string, descripcion:string, foto_nombre:string, foto_mime_type: string, foto_tamanio_bytes: string, foto_extension: string)
+ DesactivarServicio(Ser_ID:integer)
}
class ContenidoWeb {
- Contenido_ID: integer
- QuienesSomos: string
+ EditarUbicacion(id : integer, string)
+ EditarServicio(Ser_ID:integer, titulo:string, descripcion:string, foto_nombre:string, foto_mime_type: string, foto_tamanio_bytes: string, foto_extension: string)
+ EditarQuienesSomos(string)
}
class Ubicacion {
- Ubi_ID: integer
- Ubi_Link: string
+ Ubicacion (id : integer, string)
+ AddUbi(Ubicacion) : void
+ EditarUbi(id : integer, string) : void
- BuscarIDUbicacion(Ubicacion) : integer
- GuardarUbi(id : integer, string) : void
+ EliminarUbicacion(id : integer) : void
}
class Foto {
- Foto_ID: integer
- Foto_Extension: string
- Foto_Tamanio_bytes: integer
- Foto_mime_type: string
- Foto_Nombre: string
+ Foto(foto_nombre:string, foto_mime_type: string, foto_tamanio_bytes: string, foto_extension: string)
+ GuardarFoto(Per_DNI : integer, Ser_ID : integer, Foto) //si la foto corresponde a una persona Ser_ID es null, si la foto correasponde a un servicio Per_DNI es null
- GuardarRelacionConPersona(Foto_ID: integer, Per_DNI: integer)
- GuardarRelacionConServicio(Foto_ID: integer, Ser_ID: integer)
+ EditarFoto(Per_DNI : integer, Ser_ID : integer, Foto)
- BuscarIDFoto(DNI : integer) : integer
}
class DocumentacionCliente {
- Doc_ID: integer
- Doc_Nombre: string
- Doc_mimetype: string
- Doc_Tamanio_bytes: integer
- Doc_Extension: string
+ DocumentacionCliente(doc_nombre:string, doc_mime_type: string, doc_tamanio_bytes: string, doc_extension: string)
+ AgregarDocumentos(DocumentacionClienteProf_DNI, Prof_DNI : integer, Prof_Matricula : integer, Cli_DNI: integer)
+ EliminarDocumento()
+ EditarDocumento(ID_Documento : integer, Prof_DNI: integer, Cli_DNI: integer, DocumentacionCliente) //Hago una nueva instancia y la reemplazo en la base de datos
}
package "Solicitudes" {
class Formulario {
- Form_ID: integer
- Form_Descripcion: string
- Form_NombreCompleto: string
- Form_Correo: string
- Form_Celular: integer
- Form_DNIProfPref: integer
- Form_MatriculaProfPref: integer
- FechaEnvio: date
+ Formulario(integer,string,string, string, integer, integer,integer,date)
+ VerEstadoFormulario(ID_Formulario : integer) : string
- BuscarIDFormulario(Formulario) : integer
+ MostrarFormularios () : array
+ Devolver(Profesional) : void
+ Aceptar(Profesional) : void
+ Rechazar(Profesional) : void
+ VerFormulario (ID_Formulario : integer) : Formulario
- CambiarEstadoConProf(Profesional, ID_Formulario : integer, estado : string) : void
}
class HorarioPreferencia {
- Horario_ID: integer
- Horario_Descripcion: string
+ HorarioPreferencia(id : integer, descripcion : string)
}
class DiaPreferencia {
- Dia_ID: integer
- Dia_Descripcion: string
+ DiaPreferencia(id : integer, descripcion : string)
}
class Modalidad {
- Modalidad_ID: integer
- Modalidad_Descripcion: string
+ Modalidad(id : integer, descripcion : string)
}
}
' --- RELACIONES ---
' Persona
Persona "1" o-- "1..*" Profesional
Persona "1" o-- "1" Cliente
Persona "1" o-- "1" Administrador
Persona "1" -- "1" Foto
Persona "1..*" -- "1..*" Telefono
'AuthController
AuthController ..> Persona
AuthController ..> INotificadorUsuario
AuthController ..> CredencialCliente
' Autenticación
Cliente "1" -- "1" CredencialCliente
Profesional "1" -- "1" CredencialProfesional
Administrador "1" -- "1" CredencialProfesional
' Profesional
Profesional "1..*" -- "1" Profesion
Profesional "1" ...> "1..*" Turno
Profesional "1..*" -- "1" EstadoProfesional
Profesional "1..*" -- "1..*" Cliente
Profesional "1" ...> "1..*" DocumentacionCliente
Profesional "1" -- "1" Agenda
Profesional "1..*" -- "1..*" Servicio
Profesional "1..*" -- "1..*" Formulario
' Cliente
Cliente "1" ...> "1..*" Formulario
Cliente "1" -- "1..*" Turno
Cliente "1" -- "1..*" DocumentacionCliente
' Profesion, Servicio y ContenidoWeb
Profesion "1" *-- "1..*" Servicio
Profesion "1" -- "1..*" Formulario
Servicio "1" -- "1" Foto
Servicio "1..*" -- "1" ContenidoWeb
Servicio "1" -- "1..*" Formulario
ContenidoWeb "1" -- "1..*" Ubicacion
' Formulario
Formulario "1..*" -- "1..*" HorarioPreferencia
Formulario "1..*" -- "1..*" DiaPreferencia
Formulario "1..*" -- "1" Modalidad
' Turnos y Agenda
Turno "1..*" -- "1" EstadoTurno
Turno "1..*" -- "1" Agenda
Agenda "1" *-- "1..*" DiaDeAtencion
Agenda "1" *-- "0..*" ModoVacaciones
Agenda "1" *-- "0..*" Feriados
DiaDeAtencion "1" *-- "1..*" HorarioAtencion
DiaDeAtencion "1" *-- "0..*" HorarioReceso
' Administrador
Administrador "1..*" ...> "1" Servicio
Administrador "1..*" ...> "1" Profesion
Administrador "1" -- "1" Profesional
Administrador "1" ...> "1" ContenidoWeb
'Interfaces
Profesional ...> INotificadorTurnos
Cliente ...> INotificadorTurnos
Profesional ...> INotificadorCrearCliente
Administrador ...> INotificadorCrearProfesional
Profesional ...> INotificadorUsuario
Administrador ...> INotificadorUsuario
Cliente ...> INotificadorUsuario
ServicioMail ..|> INotificadorTurnos
ServicioMail ..|> INotificadorCrearCliente
ServicioMail ..|> INotificadorCrearProfesional
ServicioMail ..|> INotificadorUsuario
@enduml
+81
View File
@@ -0,0 +1,81 @@
ENTIDAD/RELACION EXPLICACION
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
DocumentacionCliente -DNICreador y MatriculaCreador: Si una persona tiene dos o más profesiones, al momento de consultar los documentos del cliente solo deben aparecer aquellos documentos que pertenecen a la profesion del usuario donde se está consultando
Ejemplo: Luciano es Abogado y Analista. Si quiero consultar los datos de un cliente desde el perfil de Analista, solo deben aparecer los documentos pertenecientes al perfil de analista (los documentos que se subieron desde el perfil de abogado, no deben aparecer)
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
EstadoProfesional -En vez de "Eliminar" a un profesional, simplemente se le cambia el estado de "Activo" a "Baja". De esta forma se mantienen las referencias a otras tablas (logs, documentacion, etc).
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
Profesional -La Primary key de Profesional es la matricula y el id de la profesion (Es imposible que esta combinación se repita. No puede haber dos abogados con la misma matricula. Pero puede haber un abogado y un contador con la misma matricula)
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
CredencialProfesional - CredProf_Usuario. Será la combinacion de: 'DNI de la persona' '-' 'codigo de profesion'.
Ejemplo: Luciano es Contador (codigo=3) y tiene DNI=12345678. Usuario = 12345678-3
- ROL: Sirve para saber a donde redireccionar en caso de que el login sea correcto (redirigir al perfil del administrador, o redirigir al perfil de un profesional)
-Cred_Token: Acá se va a almacenar el token de recuperación (es null por defecto)
-Cred_FechaHora: Acá se va a almacenar fecha y hora de la creacion del token (no puede durar para siempre, por lo general solo dura 1 hora)
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
CredencialCliente -CredCli_Token: Acá se va a almacenar el token de recuperación (es null por defecto)
-CredCli_FechaHora: Acá se va a almacenar fecha y hora de la creacion del token (no puede durar para siempre, por lo general solo dura 1 hora)
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
Profesion -VisibleEnFormulario: Es posible que en el futuro hayan Profesiones que no queramos que aparezcan como opcion en los formularios para solicitar un turno (Ej: Secretario, Técnico Informático, etc).
Además, si en el futuro se desea dar de baja esa profesion, simplemente se cambia VisibleEnFormulario a "No Visible".
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
Servicio -Estado: En vez de eliminar un servicio, simplemente se desactiva o se da de baja. En caso de que hayan formularios vinculados a ese servicio, no quedarían huerfanos
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
Persona -CorreoRecuperacion: Al momento de querer recuperar la contraseña o usuario, el usuario recibirá en su correo principal un link para cambiar su contraseña. En caso de perder acceso a su correo principal, se le enviará el link a un correo alternativo.
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
RELACION ENTRE: Formulario y Profesional -EstadoFormulario: Cada profesional tendra la posibilidad de Ignorar, Aceptar y Rechazar el formulario, por lo tanto, cada uno tendrá un mismo formulario con diferentes estados.
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
RELACION ENTRE: Profesional y Formulario -Si el formulario fue aceptado por algun profesional, automaticamente tendrá el estado "No disponible" (para aquellos profesionales que no aceptaron el formulario o nunca lo abrieron). Y tendra el estado de "Aceptado" para el profesional que lo aceptó.
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
Agenda -Si la agenda está en modo "vacaciones", entonces la asignación automática de turnos, ignora el periodo de vacaciones.
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
DiaDeAtencion -El dia de la semana tendrá una hora de inicio (Dia_Comienzo) y una hora de finalización (Dia_Fin). Ejemplo: El dia Lunes (Dia_Descripcion=Lunes, Dia_ID=1) comenzará a las 7AM (Dia_Comienzo=7) y terminará a las 14PM (Dia_Fin=14)
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
Turno -Turno_Inicio: Tendrá la hora de inicio del turno y la fecha del turno. (El fin del turno se calcularia Fin_Turno = Turno_Inicio + DuracionTurno)
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
RELACION ENTRE: Profesional y Cliente -Si en el futuro un profesional decide dar de baja a un Cliente, simplemente se cambia el estado de EstadoRelacion de "Activo" a "Baja". Si ese cliente tiene relacion con otros profesionales, esa relacion no se verá afectada.
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
Formulario -Si Cli_DNI es null, entonces ese formulario lo envió un Visitante, de lo contrario lo envió un Cliente.
-Form_Estado: Si aún ningún profesional ha aceptado este formulario, el estado será Disponible. Si ya lo aceptó algún profesional, será No disponible.
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
LogSeguridad -Responsable_ID: Vendría a ser el DNI de la persona que ejecutó esa accion.
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
HorariosAtenciones -TIPO = Corresponde a AM o PM
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
Baja -Su funcion es habilitar o deshabilitar credenciales. Si el Cliente/profesional esta "Activo" puede iniciar sesión. Si está de "Baja", no podrá iniciar sesión. (El administrador decide quien esta de alta o baja)
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
Binary file not shown.
+3
View File
@@ -0,0 +1,3 @@
Version 1.0.0
Sistema funcionando sin fallas hasta el momento.
+163 -39
View File
@@ -1,59 +1,183 @@
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p> # Abogadas Litoral
<p align="center"> Aplicacion web para gestion de turnos de un estudio juridico, desarrollada con Laravel y Vite.
<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
</p>
## About Laravel ## Tabla de contenido
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as: - [Tecnologias y versiones](#tecnologias-y-versiones)
- [Requisitos previos](#requisitos-previos)
- [Instalacion](#instalacion)
- [Comandos utiles](#comandos-utiles)
- [Testing](#testing)
- [Flujo de trabajo con Git](#flujo-de-trabajo-con-git)
- [Estructura principal](#estructura-principal)
- [Notas](#notas)
- [Simple, fast routing engine](https://laravel.com/docs/routing). ## Tecnologias y versiones
- [Powerful dependency injection container](https://laravel.com/docs/container).
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
- [Robust background job processing](https://laravel.com/docs/queues).
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
Laravel is accessible, powerful, and provides tools required for large, robust applications. Versiones tomadas del proyecto actual:
## Learning Laravel - PHP: `^8.2`
- Laravel Framework: `^12.0`
- PHPUnit: `^11.5.3`
- Node.js: recomendado `>=20`
- npm: recomendado `>=10`
- Vite: `^7.0.7`
- Bootstrap: `^5.3.8`
- FullCalendar: `^6.1.20`
- Spatie Laravel Backup: `^9.3`
- DomPDF (barryvdh/laravel-dompdf): `^3.1`
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework. You can also check out [Laravel Learn](https://laravel.com/learn), where you will be guided through building a modern Laravel application. Archivos fuente de versionado:
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library. - `composer.json`
- `package.json`
## Laravel Sponsors ## Requisitos previos
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com). - PHP 8.2 o superior
- Composer 2.x
- Node.js y npm
- Base de datos (segun entorno):
- En `.env.example` la conexion por defecto es `sqlite`
- En `config/database.php` el fallback de Laravel esta en `mysql`
### Premium Partners ## Instalacion
- **[Vehikl](https://vehikl.com)** ### Opcion rapida (script del proyecto)
- **[Tighten Co.](https://tighten.co)**
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
- **[64 Robots](https://64robots.com)**
- **[Curotec](https://www.curotec.com/services/technologies/laravel)**
- **[DevSquad](https://devsquad.com/hire-laravel-developers)**
- **[Redberry](https://redberry.international/laravel-development)**
- **[Active Logic](https://activelogic.com)**
## Contributing ```bash
composer run setup
```
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions). Este script ejecuta:
## Code of Conduct 1. `composer install`
2. Creacion de `.env` desde `.env.example` (si no existe)
3. `php artisan key:generate`
4. `php artisan migrate --force`
5. `npm install`
6. `npm run build`
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct). ### Opcion manual
## Security Vulnerabilities ```bash
composer install
copy .env.example .env
php artisan key:generate
php artisan migrate
npm install
npm run build
```
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed. Si usas Linux/Mac, reemplaza `copy` por:
## License ```bash
cp .env.example .env
```
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). ## Comandos utiles
Levantar entorno de desarrollo completo (servidor, cola, logs y Vite):
```bash
composer run dev
```
Levantar solo backend:
```bash
php artisan serve
```
Levantar solo frontend:
```bash
npm run dev
```
Compilar assets para produccion:
```bash
npm run build
```
## Testing
Ejecutar tests:
```bash
composer run test
```
o
```bash
php artisan test
```
## Flujo de trabajo con Git
### 1) Crear rama de trabajo
```bash
git checkout main
git pull origin main
git checkout -b feature/nombre-cambio
```
### 2) Commits pequenos y descriptivos
Formato recomendado:
- `feat: agrega agenda semanal`
- `fix: corrige validacion de telefono`
- `docs: actualiza readme de instalacion`
### 3) Subir rama y abrir Pull Request
```bash
git add .
git commit -m "feat: descripcion breve"
git push -u origin feature/nombre-cambio
```
### 4) Actualizar tu rama con cambios de main
```bash
git checkout main
git pull origin main
git checkout feature/nombre-cambio
git merge main
```
## Estructura principal
- `app/`: modelos, controladores, middleware y logica principal
- `resources/views/`: vistas Blade
- `resources/js/` y `resources/css/`: frontend
- `routes/`: rutas web, api y consola
- `database/migrations/`: migraciones
- `tests/`: pruebas unitarias y feature
- `scripts/`: utilidades de scheduler en Windows
## Datos de acceso (Entorno de desarrollo)
Las credenciales de administrador se generan automáticamente al ejecutar las migraciones con seeders:
```bash
php artisan migrate:fresh --seed
```
O si prefieres solo los seeders sin resetear migraciones:
```bash
php artisan db:seed
```
Consulta `database/seeders/` para ver los datos que se cargan.
## Notas
- No subir archivos sensibles al repositorio (`.env`, claves, tokens).
- Mantener este README actualizado ante cambios de version o arquitectura.
+73
View File
@@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
use App\Models\Administrador;
use App\Models\CredencialProfesional;
use App\Models\Foto;
use App\Models\Persona;
use Illuminate\Contracts\Console\Kernel;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
require __DIR__ . '/vendor/autoload.php';
$app = require __DIR__ . '/bootstrap/app.php';
$app->make(Kernel::class)->bootstrap();
$usuario = env('ADMIN_USUARIO', 'admin');
$passwordPlano = env('ADMIN_PASSWORD', 'admin1234');
$correo = env('ADMIN_CORREO', 'admin@abogadaslitoral.com');
$dni = env('ADMIN_DNI', '1');
$nombre = env('ADMIN_NOMBRE', 'Usuario');
$apellido = env('ADMIN_APELLIDO', 'Administrador');
$cuil = env('ADMIN_CUIL', '20-1-0');
$fechaNac = env('ADMIN_FECHANAC', '2026-01-01');
try {
DB::transaction(function () use ($usuario, $passwordPlano, $correo, $dni, $nombre, $apellido, $cuil, $fechaNac): void {
$foto = Foto::firstOrCreate(
['ruta' => 'avatars/admin-default.png'],
[
'extension' => 'png',
'nombre' => 'admin-default',
'mime_type' => 'image/png',
'tamanio_bytes' => 0,
]
);
$persona = Persona::updateOrCreate(
['dni' => $dni],
[
'nombre' => $nombre,
'apellido' => $apellido,
'cuil' => $cuil,
'fechanac' => $fechaNac,
'foto_id' => $foto->id,
]
);
$credencial = CredencialProfesional::updateOrCreate(
['usuario' => $usuario],
[
'contra' => Hash::make($passwordPlano),
'rol' => 'ADMIN',
]
);
Administrador::updateOrCreate(
['dni' => $dni, 'correo' => $correo],
[
'persona_id' => $persona->id,
'credencialprofesional_id' => $credencial->id,
]
);
});
echo "Administrador creado/actualizado correctamente." . PHP_EOL;
echo "Usuario: {$usuario}" . PHP_EOL;
echo "Correo: {$correo}" . PHP_EOL;
} catch (Throwable $e) {
fwrite(STDERR, 'Error al crear administrador: ' . $e->getMessage() . PHP_EOL);
exit(1);
}
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\AccionLog;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class AccionLogController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = AccionLog::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new AccionLog())->getFillable());
$accionLog = AccionLog::create($payload);
return response()->json([
'success' => true,
'data' => $accionLog,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(AccionLog $accionLog): JsonResponse
{
return response()->json([
'success' => true,
'data' => $accionLog,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, AccionLog $accionLog): JsonResponse
{
$payload = $request->only((new AccionLog())->getFillable());
$accionLog->update($payload);
return response()->json([
'success' => true,
'data' => $accionLog,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(AccionLog $accionLog): JsonResponse
{
$accionLog->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\Administrador;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class AdministradorController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = Administrador::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new Administrador())->getFillable());
$administrador = Administrador::create($payload);
return response()->json([
'success' => true,
'data' => $administrador,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(Administrador $administrador): JsonResponse
{
return response()->json([
'success' => true,
'data' => $administrador,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Administrador $administrador): JsonResponse
{
$payload = $request->only((new Administrador())->getFillable());
$administrador->update($payload);
return response()->json([
'success' => true,
'data' => $administrador,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Administrador $administrador): JsonResponse
{
$administrador->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
+80
View File
@@ -0,0 +1,80 @@
<?php
namespace App\Http\Controllers;
use App\Models\Agenda;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class AgendaController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = Agenda::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new Agenda())->getFillable());
$agenda = Agenda::create($payload);
return response()->json([
'success' => true,
'data' => $agenda,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(Agenda $agenda): JsonResponse
{
return response()->json([
'success' => true,
'data' => $agenda,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Agenda $agenda): JsonResponse
{
$payload = $request->only((new Agenda())->getFillable());
$agenda->update($payload);
return response()->json([
'success' => true,
'data' => $agenda,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Agenda $agenda): JsonResponse
{
$agenda->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
+463
View File
@@ -0,0 +1,463 @@
<?php
namespace App\Http\Controllers;
use App\Models\AccionLog;
use App\Models\CredencialCliente;
use App\Models\CredencialProfesional;
use App\Models\LogSeguridad;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Str;
class AuthController extends Controller
{
public function loginClienteWeb(Request $request): RedirectResponse
{
$request->validate([
'correo' => ['required', 'string'],
'contra' => ['required', 'string'],
'website' => ['nullable', 'string', 'max:255'],
]);
if (!$this->honeypotValido($request)) {
return back()
->withInput($request->except(['contra', 'website']))
->withErrors(['login_error' => 'No se pudo procesar el inicio de sesión.']);
}
$correo = trim((string) $request->input('correo'));
$contra = (string) $request->input('contra');
$credencial = CredencialCliente::with('cliente.persona.telefonos')->where('correo', $correo)->first();
if (!$credencial || !$this->credencialValida($contra, (string) $credencial->contra)) {
return back()
->withInput($request->except('contra'))
->with('login_error', 'Usuario o contraseña incorrectos.');
}
if ((int) ($credencial->cliente?->baja_id ?? 0) !== 1) {
return back()
->withInput($request->except('contra'))
->with('login_error', 'No es posible iniciar sesion. Comuníquese con un profesional de Abogadas del Litoral');
}
$token = Str::random(64);
$credencial->token = $token;
$credencial->fecha_hora = now();
$credencial->save();
$personaCliente = $credencial->cliente?->persona;
$nombreCliente = trim((string) ($personaCliente?->nombre ?? ''));
$apellidoCliente = trim((string) ($personaCliente?->apellido ?? ''));
$celularCliente = trim((string) ($personaCliente?->telefonos?->first()?->telefono ?? ''));
$request->session()->regenerate();
$request->session()->regenerateToken();
$request->session()->put([
'cliente_auth' => true,
'cliente_id' => (int) ($credencial->cliente?->id ?? 0),
'cliente_token' => $token,
'cliente_correo' => (string) $credencial->correo,
'cliente_nombre' => $nombreCliente !== '' ? $nombreCliente : (string) $credencial->correo,
'cliente_apellido' => $apellidoCliente,
'cliente_celular' => $celularCliente,
]);
$this->registrarAccionSeguridad(
(int) ($credencial->cliente?->persona_id ?? 0),
'CLIENTE',
(string) $request->ip(),
'Inició sesión',
'El cliente ID ' . (int) ($credencial->cliente?->id ?? 0) . ' inició sesión.'
);
$this->limpiarThrottle($request, 'login-cliente-web');
return redirect('/cliente/dashboard')->with('login_success', 'Login exitoso.');
}
public function loginPersonalWeb(Request $request): RedirectResponse
{
$request->validate([
'usuario' => ['required', 'string'],
'contra' => ['required', 'string'],
'website' => ['nullable', 'string', 'max:255'],
]);
if (!$this->honeypotValido($request)) {
return back()
->withInput($request->except(['contra', 'website']))
->withErrors(['login_error' => 'No se pudo procesar el inicio de sesión.']);
}
$usuario = trim((string) $request->input('usuario'));
$contra = (string) $request->input('contra');
$credencial = CredencialProfesional::with(['administrador.persona', 'profesional.persona'])
->where('usuario', $usuario)
->first();
if (!$credencial || !$this->credencialValida($contra, (string) $credencial->contra)) {
return back()
->withInput($request->except('contra'))
->with('login_error', 'Usuario o contraseña incorrectos.');
}
if (strtoupper((string) $credencial->rol) !== 'ADMIN' && (int) ($credencial->profesional?->baja_id ?? 0) !== 1) {
return back()
->withInput($request->except('contra'))
->with('login_error', 'Usted está dado de baja. Comuníquese con el administrador');
}
$token = Str::random(64);
$credencial->token = $token;
$credencial->fecha_hora = now();
$credencial->save();
$rol = strtoupper((string) $credencial->rol);
$personaProfesional = $credencial->profesional?->persona;
$nombreProfesional = trim((string) ($personaProfesional?->nombre ?? '') . ' ' . (string) ($personaProfesional?->apellido ?? ''));
$personaAdmin = $credencial->administrador?->persona;
$nombreAdmin = trim((string) ($personaAdmin?->nombre ?? '') . ' ' . (string) ($personaAdmin?->apellido ?? ''));
$nombreSesion = $rol === 'ADMIN' ? $nombreAdmin : $nombreProfesional;
$request->session()->regenerate();
$request->session()->regenerateToken();
$request->session()->put([
'personal_auth' => true,
'personal_token' => $token,
'personal_usuario' => (string) $credencial->usuario,
'personal_nombre' => $nombreSesion !== '' ? $nombreSesion : (string) $credencial->usuario,
'personal_apellido' => $rol === 'ADMIN'
? ($personaAdmin?->apellido ?? '')
: ($personaProfesional?->apellido ?? ''),
'personal_rol' => $rol,
'personal_credencial_id' => (int) $credencial->id,
]);
$this->registrarAccionSeguridad(
$rol === 'ADMIN'
? (int) ($credencial->administrador?->persona_id ?? 0)
: (int) ($credencial->profesional?->persona_id ?? 0),
$rol === 'ADMIN' ? 'ADMIN' : 'PROFESIONAL',
(string) $request->ip(),
'Inició sesión',
($rol === 'ADMIN' ? 'La administradora ID ' . (int) ($credencial->administrador?->id ?? 0) : 'El profesional ID ' . (int) ($credencial->profesional?->id ?? 0)) . ' inició sesión.'
);
$this->limpiarThrottle($request, 'login-personal-web');
$redirectPath = $rol === 'ADMIN' ? '/administrador/dashboard' : '/profesional/dashboard';
return redirect($redirectPath)->with('login_success', 'Login exitoso.');
}
public function loginCliente(Request $request): JsonResponse
{
$request->validate([
'correo' => ['required', 'string'],
'contra' => ['required', 'string'],
]);
$correo = trim((string) $request->input('correo'));
$contra = (string) $request->input('contra');
$credencial = CredencialCliente::with('cliente')->where('correo', $correo)->first();
if (!$credencial || !$this->credencialValida($contra, (string) $credencial->contra)) {
return response()->json([
'success' => false,
'message' => 'Credenciales invalidas',
], 401);
}
if ((int) ($credencial->cliente?->baja_id ?? 0) !== 1) {
return response()->json([
'success' => false,
'message' => 'No es posible iniciar sesion. Comuníquese con un profesional de Abogadas del Litoral',
], 403);
}
$token = Str::random(64);
$credencial->token = $token;
$credencial->fecha_hora = now();
$credencial->save();
$this->registrarAccionSeguridad(
(int) ($credencial->cliente?->persona_id ?? 0),
'CLIENTE',
(string) $request->ip(),
'Inició sesión',
'El cliente ID ' . (int) ($credencial->cliente?->id ?? 0) . ' inició sesión.'
);
$this->limpiarThrottle($request, 'login-cliente-api');
return response()->json([
'success' => true,
'data' => [
'tipo' => 'cliente',
'id_credencial' => $credencial->id,
'token' => $token,
],
'message' => 'Login de cliente exitoso',
], 200);
}
public function loginPersonal(Request $request): JsonResponse
{
$request->validate([
'usuario' => ['required', 'string'],
'contra' => ['required', 'string'],
]);
$usuario = trim((string) $request->input('usuario'));
$contra = (string) $request->input('contra');
$credencial = CredencialProfesional::with(['administrador', 'profesional'])->where('usuario', $usuario)->first();
if (!$credencial || !$this->credencialValida($contra, (string) $credencial->contra)) {
return response()->json([
'success' => false,
'message' => 'Credenciales invalidas',
], 401);
}
if (strtoupper((string) $credencial->rol) !== 'ADMIN' && (int) ($credencial->profesional?->baja_id ?? 0) !== 1) {
return response()->json([
'success' => false,
'message' => 'Usted está dado de baja. Comuníquese con el administrador',
], 403);
}
$token = Str::random(64);
$credencial->token = $token;
$credencial->fecha_hora = now();
$credencial->save();
$rol = strtoupper((string) $credencial->rol);
$this->registrarAccionSeguridad(
$rol === 'ADMIN'
? (int) ($credencial->administrador?->persona_id ?? 0)
: (int) ($credencial->profesional?->persona_id ?? 0),
$rol === 'ADMIN' ? 'ADMIN' : 'PROFESIONAL',
(string) $request->ip(),
'Inició sesión',
($rol === 'ADMIN' ? 'La administradora ID ' . (int) ($credencial->administrador?->id ?? 0) : 'El profesional ID ' . (int) ($credencial->profesional?->id ?? 0)) . ' inició sesión.'
);
$this->limpiarThrottle($request, 'login-personal-api');
return response()->json([
'success' => true,
'data' => [
'tipo' => 'personal',
'rol' => $credencial->rol,
'id_credencial' => $credencial->id,
'token' => $token,
],
'message' => 'Login de personal exitoso',
], 200);
}
public function login(Request $request): JsonResponse
{
$request->validate([
'identificador' => ['required', 'string'],
'contra' => ['required', 'string'],
'tipo' => ['nullable', 'in:cliente,profesional'],
]);
$identificador = trim((string) $request->input('identificador'));
$contra = (string) $request->input('contra');
$tipo = $request->input('tipo');
$credencial = null;
$tipoDetectado = null;
if ($tipo === 'cliente') {
$credencial = CredencialCliente::with('cliente')->where('correo', $identificador)->first();
$tipoDetectado = 'cliente';
} elseif ($tipo === 'profesional') {
$credencial = CredencialProfesional::where('usuario', $identificador)->first();
$tipoDetectado = 'personal';
} else {
$credencial = CredencialCliente::with('cliente')->where('correo', $identificador)->first();
$tipoDetectado = $credencial ? 'cliente' : null;
if (!$credencial) {
$credencial = CredencialProfesional::where('usuario', $identificador)->first();
$tipoDetectado = $credencial ? 'personal' : null;
}
}
if (!$credencial || !$this->credencialValida($contra, (string) $credencial->contra)) {
return response()->json([
'success' => false,
'message' => 'Credenciales invalidas',
], 401);
}
if ($credencial instanceof CredencialCliente
&& (int) ($credencial->cliente?->baja_id ?? 0) !== 1) {
return response()->json([
'success' => false,
'message' => 'No es posible iniciar sesion. Comuníquese con un profesional de Abogadas del Litoral',
], 403);
}
if ($credencial instanceof CredencialProfesional
&& strtoupper((string) $credencial->rol) !== 'ADMIN'
&& (int) ($credencial->profesional?->baja_id ?? 0) !== 1) {
return response()->json([
'success' => false,
'message' => 'Usted está dado de baja. Comuníquese con el administrador',
], 403);
}
$token = Str::random(64);
$credencial->token = $token;
$credencial->fecha_hora = now();
$credencial->save();
if ($credencial instanceof CredencialCliente) {
$this->registrarAccionSeguridad(
(int) ($credencial->cliente?->persona_id ?? 0),
'CLIENTE',
(string) $request->ip(),
'Inició sesión',
'El cliente ID ' . (int) ($credencial->cliente?->id ?? 0) . ' inició sesión.'
);
} elseif ($credencial instanceof CredencialProfesional) {
$rolDescripcion = strtoupper((string) $credencial->rol) === 'ADMIN' ? 'ADMIN' : 'PROFESIONAL';
$this->registrarAccionSeguridad(
$rolDescripcion === 'ADMIN'
? (int) ($credencial->administrador?->persona_id ?? 0)
: (int) ($credencial->profesional?->persona_id ?? 0),
$rolDescripcion,
(string) $request->ip(),
'Inició sesión',
($rolDescripcion === 'ADMIN' ? 'La administradora ID ' . (int) ($credencial->administrador?->id ?? 0) : 'El profesional ID ' . (int) ($credencial->profesional?->id ?? 0)) . ' inició sesión.'
);
}
$this->limpiarThrottle($request, 'login-api-general');
return response()->json([
'success' => true,
'data' => [
'tipo' => $tipoDetectado,
'rol' => $credencial instanceof CredencialProfesional ? $credencial->rol : null,
'id_credencial' => $credencial->id,
'token' => $token,
],
'message' => 'Login exitoso',
], 200);
}
private function honeypotValido(Request $request): bool
{
return trim((string) $request->input('website', '')) === '';
}
public function logout(Request $request): JsonResponse
{
$token = (string) $request->input('token', '');
if ($token === '') {
return response()->json([
'success' => false,
'message' => 'Token requerido',
], 422);
}
$credencialCliente = CredencialCliente::with('cliente')->where('token', $token)->first();
if ($credencialCliente) {
$this->registrarAccionSeguridad(
(int) ($credencialCliente->cliente?->persona_id ?? 0),
'CLIENTE',
(string) $request->ip(),
'Cerró sesión',
'El cliente ID ' . (int) ($credencialCliente->cliente?->id ?? 0) . ' cerró sesión.'
);
$credencialCliente->token = null;
$credencialCliente->fecha_hora = now();
$credencialCliente->save();
return response()->json([
'success' => true,
'message' => 'Logout exitoso',
], 200);
}
$credencialProfesional = CredencialProfesional::with(['administrador', 'profesional'])->where('token', $token)->first();
if ($credencialProfesional) {
$rol = strtoupper((string) $credencialProfesional->rol);
$this->registrarAccionSeguridad(
$rol === 'ADMIN'
? (int) ($credencialProfesional->administrador?->persona_id ?? 0)
: (int) ($credencialProfesional->profesional?->persona_id ?? 0),
$rol === 'ADMIN' ? 'ADMIN' : 'PROFESIONAL',
(string) $request->ip(),
'Cerró sesión',
($rol === 'ADMIN' ? 'La administradora ID ' . (int) ($credencialProfesional->administrador?->id ?? 0) : 'El profesional ID ' . (int) ($credencialProfesional->profesional?->id ?? 0)) . ' cerró sesión.'
);
$credencialProfesional->token = null;
$credencialProfesional->fecha_hora = now();
$credencialProfesional->save();
return response()->json([
'success' => true,
'message' => 'Logout exitoso',
], 200);
}
return response()->json([
'success' => false,
'message' => 'Token invalido',
], 401);
}
private function credencialValida(string $contraIngresada, string $contraGuardada): bool
{
if ($contraIngresada === $contraGuardada) {
return true;
}
return Hash::check($contraIngresada, $contraGuardada);
}
private function limpiarThrottle(Request $request, string $prefijo): void
{
RateLimiter::clear(md5($prefijo . $request->ip()));
}
private function registrarAccionSeguridad(?int $personaId, string $rol, string $ipOrigen, string $accionDescripcion, string $descripcion): void
{
if (!$personaId) {
return;
}
$accion = AccionLog::query()->firstWhere('descripcion', $accionDescripcion);
if (!$accion) {
return;
}
LogSeguridad::create([
'descripcion' => $descripcion,
'fechahora' => now(),
'IPorigen' => $ipOrigen,
'rol' => $rol,
'persona_id' => (int) $personaId,
'accion_id' => (int) $accion->id,
]);
}
}
+79
View File
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\Baja;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class BajaController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = Baja::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new Baja())->getFillable());
$baja = Baja::create($payload);
return response()->json([
'success' => true,
'data' => $baja,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(Baja $baja): JsonResponse
{
return response()->json([
'success' => true,
'data' => $baja,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Baja $baja): JsonResponse
{
$payload = $request->only((new Baja())->getFillable());
$baja->update($payload);
return response()->json([
'success' => true,
'data' => $baja,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Baja $baja): JsonResponse
{
$baja->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
+79
View File
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\Bug;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class BugController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = Bug::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new Bug())->getFillable());
$bug = Bug::create($payload);
return response()->json([
'success' => true,
'data' => $bug,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(Bug $bug): JsonResponse
{
return response()->json([
'success' => true,
'data' => $bug,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Bug $bug): JsonResponse
{
$payload = $request->only((new Bug())->getFillable());
$bug->update($payload);
return response()->json([
'success' => true,
'data' => $bug,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Bug $bug): JsonResponse
{
$bug->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\Cliente;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ClienteController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = Cliente::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new Cliente())->getFillable());
$cliente = Cliente::create($payload);
return response()->json([
'success' => true,
'data' => $cliente,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(Cliente $cliente): JsonResponse
{
return response()->json([
'success' => true,
'data' => $cliente,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Cliente $cliente): JsonResponse
{
$payload = $request->only((new Cliente())->getFillable());
$cliente->update($payload);
return response()->json([
'success' => true,
'data' => $cliente,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Cliente $cliente): JsonResponse
{
$cliente->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\ContenidoWeb;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ContenidoWebController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = ContenidoWeb::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new ContenidoWeb())->getFillable());
$contenidoWeb = ContenidoWeb::create($payload);
return response()->json([
'success' => true,
'data' => $contenidoWeb,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(ContenidoWeb $contenidoWeb): JsonResponse
{
return response()->json([
'success' => true,
'data' => $contenidoWeb,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, ContenidoWeb $contenidoWeb): JsonResponse
{
$payload = $request->only((new ContenidoWeb())->getFillable());
$contenidoWeb->update($payload);
return response()->json([
'success' => true,
'data' => $contenidoWeb,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(ContenidoWeb $contenidoWeb): JsonResponse
{
$contenidoWeb->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\CredencialCliente;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class CredencialClienteController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = CredencialCliente::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new CredencialCliente())->getFillable());
$credencialCliente = CredencialCliente::create($payload);
return response()->json([
'success' => true,
'data' => $credencialCliente,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(CredencialCliente $credencialCliente): JsonResponse
{
return response()->json([
'success' => true,
'data' => $credencialCliente,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, CredencialCliente $credencialCliente): JsonResponse
{
$payload = $request->only((new CredencialCliente())->getFillable());
$credencialCliente->update($payload);
return response()->json([
'success' => true,
'data' => $credencialCliente,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(CredencialCliente $credencialCliente): JsonResponse
{
$credencialCliente->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\CredencialProfesional;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class CredencialProfesionalController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = CredencialProfesional::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new CredencialProfesional())->getFillable());
$credencialProfesional = CredencialProfesional::create($payload);
return response()->json([
'success' => true,
'data' => $credencialProfesional,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(CredencialProfesional $credencialProfesional): JsonResponse
{
return response()->json([
'success' => true,
'data' => $credencialProfesional,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, CredencialProfesional $credencialProfesional): JsonResponse
{
$payload = $request->only((new CredencialProfesional())->getFillable());
$credencialProfesional->update($payload);
return response()->json([
'success' => true,
'data' => $credencialProfesional,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(CredencialProfesional $credencialProfesional): JsonResponse
{
$credencialProfesional->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
+79
View File
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\Dia;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class DiaController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = Dia::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new Dia())->getFillable());
$dia = Dia::create($payload);
return response()->json([
'success' => true,
'data' => $dia,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(Dia $dia): JsonResponse
{
return response()->json([
'success' => true,
'data' => $dia,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Dia $dia): JsonResponse
{
$payload = $request->only((new Dia())->getFillable());
$dia->update($payload);
return response()->json([
'success' => true,
'data' => $dia,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Dia $dia): JsonResponse
{
$dia->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\DiaDeAtencion;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class DiaDeAtencionController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = DiaDeAtencion::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new DiaDeAtencion())->getFillable());
$diaDeAtencion = DiaDeAtencion::create($payload);
return response()->json([
'success' => true,
'data' => $diaDeAtencion,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(DiaDeAtencion $diaDeAtencion): JsonResponse
{
return response()->json([
'success' => true,
'data' => $diaDeAtencion,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, DiaDeAtencion $diaDeAtencion): JsonResponse
{
$payload = $request->only((new DiaDeAtencion())->getFillable());
$diaDeAtencion->update($payload);
return response()->json([
'success' => true,
'data' => $diaDeAtencion,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(DiaDeAtencion $diaDeAtencion): JsonResponse
{
$diaDeAtencion->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\DiaPreferencia;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class DiaPreferenciaController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = DiaPreferencia::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new DiaPreferencia())->getFillable());
$diaPreferencia = DiaPreferencia::create($payload);
return response()->json([
'success' => true,
'data' => $diaPreferencia,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(DiaPreferencia $diaPreferencia): JsonResponse
{
return response()->json([
'success' => true,
'data' => $diaPreferencia,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, DiaPreferencia $diaPreferencia): JsonResponse
{
$payload = $request->only((new DiaPreferencia())->getFillable());
$diaPreferencia->update($payload);
return response()->json([
'success' => true,
'data' => $diaPreferencia,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(DiaPreferencia $diaPreferencia): JsonResponse
{
$diaPreferencia->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\DocumentacionCliente;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class DocumentacionClienteController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = DocumentacionCliente::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new DocumentacionCliente())->getFillable());
$documentacionCliente = DocumentacionCliente::create($payload);
return response()->json([
'success' => true,
'data' => $documentacionCliente,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(DocumentacionCliente $documentacionCliente): JsonResponse
{
return response()->json([
'success' => true,
'data' => $documentacionCliente,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, DocumentacionCliente $documentacionCliente): JsonResponse
{
$payload = $request->only((new DocumentacionCliente())->getFillable());
$documentacionCliente->update($payload);
return response()->json([
'success' => true,
'data' => $documentacionCliente,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(DocumentacionCliente $documentacionCliente): JsonResponse
{
$documentacionCliente->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
+79
View File
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\Error as ErrorModel;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ErrorController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = ErrorModel::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new ErrorModel())->getFillable());
$error = ErrorModel::create($payload);
return response()->json([
'success' => true,
'data' => $error,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(ErrorModel $error): JsonResponse
{
return response()->json([
'success' => true,
'data' => $error,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, ErrorModel $error): JsonResponse
{
$payload = $request->only((new ErrorModel())->getFillable());
$error->update($payload);
return response()->json([
'success' => true,
'data' => $error,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(ErrorModel $error): JsonResponse
{
$error->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
@@ -0,0 +1,82 @@
<?php
namespace App\Http\Controllers;
use App\Models\EstadoTurno;
use Illuminate\Http\Request;
class EstadoTurnoController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index() : JsonResponse
{
$estadoTurnos = EstadoTurno::all();
return response()->json([
'success' => true,
'data' => $estadoTurnos,
'message' => 'Estados de turno obtenidos correctamente'
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request) : JsonResponse
{
$validated = $request->validate([
'descripcion' => 'required|string|max:255|unique:estado_turnos,descripcion',
]);
$estadoTurno = EstadoTurno::create($validated);
return response()->json([
'success' => true,
'data' => $estadoTurno,
'message' => 'Estado de turno creado correctamente'
], 201);
}
/**
* Display the specified resource.
*/
public function show(EstadoTurno $estadoTurno) : JsonResponse
{
return response()->json([
'success' => true,
'data' => $estadoTurno,
'message' => 'Estado de turno obtenido correctamente'
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, EstadoTurno $estadoTurno): JsonResponse
{
$validated = $request->validate([
'descripcion' => 'required|string|max:255|unique:estado_turnos,descripcion,' . $estadoTurno->id,
]);
$estadoTurno->update($validated);
return response()->json([
'success' => true,
'data' => $estadoTurno,
'message' => 'Estado de turno actualizado correctamente'
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(EstadoTurno $estadoTurno) : JsonResponse
{
$estadoTurno->delete();
return response()->json([
'success' => true,
'message' => 'Estado de turno eliminado correctamente'
}
}
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\Feriado;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class FeriadoController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = Feriado::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new Feriado())->getFillable());
$feriado = Feriado::create($payload);
return response()->json([
'success' => true,
'data' => $feriado,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(Feriado $feriado): JsonResponse
{
return response()->json([
'success' => true,
'data' => $feriado,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Feriado $feriado): JsonResponse
{
$payload = $request->only((new Feriado())->getFillable());
$feriado->update($payload);
return response()->json([
'success' => true,
'data' => $feriado,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Feriado $feriado): JsonResponse
{
$feriado->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\Formulario;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class FormularioController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = Formulario::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new Formulario())->getFillable());
$formulario = Formulario::create($payload);
return response()->json([
'success' => true,
'data' => $formulario,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(Formulario $formulario): JsonResponse
{
return response()->json([
'success' => true,
'data' => $formulario,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Formulario $formulario): JsonResponse
{
$payload = $request->only((new Formulario())->getFillable());
$formulario->update($payload);
return response()->json([
'success' => true,
'data' => $formulario,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Formulario $formulario): JsonResponse
{
$formulario->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\FotoBug;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class FotoBugController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = FotoBug::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new FotoBug())->getFillable());
$fotoBug = FotoBug::create($payload);
return response()->json([
'success' => true,
'data' => $fotoBug,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(FotoBug $fotoBug): JsonResponse
{
return response()->json([
'success' => true,
'data' => $fotoBug,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, FotoBug $fotoBug): JsonResponse
{
$payload = $request->only((new FotoBug())->getFillable());
$fotoBug->update($payload);
return response()->json([
'success' => true,
'data' => $fotoBug,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(FotoBug $fotoBug): JsonResponse
{
$fotoBug->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
+79
View File
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\Foto;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class FotoController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = Foto::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new Foto())->getFillable());
$foto = Foto::create($payload);
return response()->json([
'success' => true,
'data' => $foto,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(Foto $foto): JsonResponse
{
return response()->json([
'success' => true,
'data' => $foto,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Foto $foto): JsonResponse
{
$payload = $request->only((new Foto())->getFillable());
$foto->update($payload);
return response()->json([
'success' => true,
'data' => $foto,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Foto $foto): JsonResponse
{
$foto->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\HorarioDeAtencion;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class HorarioDeAtencionController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = HorarioDeAtencion::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new HorarioDeAtencion())->getFillable());
$horarioDeAtencion = HorarioDeAtencion::create($payload);
return response()->json([
'success' => true,
'data' => $horarioDeAtencion,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(HorarioDeAtencion $horarioDeAtencion): JsonResponse
{
return response()->json([
'success' => true,
'data' => $horarioDeAtencion,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, HorarioDeAtencion $horarioDeAtencion): JsonResponse
{
$payload = $request->only((new HorarioDeAtencion())->getFillable());
$horarioDeAtencion->update($payload);
return response()->json([
'success' => true,
'data' => $horarioDeAtencion,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(HorarioDeAtencion $horarioDeAtencion): JsonResponse
{
$horarioDeAtencion->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\HorarioPreferencia;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class HorarioPreferenciaController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = HorarioPreferencia::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new HorarioPreferencia())->getFillable());
$horarioPreferencia = HorarioPreferencia::create($payload);
return response()->json([
'success' => true,
'data' => $horarioPreferencia,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(HorarioPreferencia $horarioPreferencia): JsonResponse
{
return response()->json([
'success' => true,
'data' => $horarioPreferencia,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, HorarioPreferencia $horarioPreferencia): JsonResponse
{
$payload = $request->only((new HorarioPreferencia())->getFillable());
$horarioPreferencia->update($payload);
return response()->json([
'success' => true,
'data' => $horarioPreferencia,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(HorarioPreferencia $horarioPreferencia): JsonResponse
{
$horarioPreferencia->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\HorarioReceso;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class HorarioRecesoController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = HorarioReceso::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new HorarioReceso())->getFillable());
$horarioReceso = HorarioReceso::create($payload);
return response()->json([
'success' => true,
'data' => $horarioReceso,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(HorarioReceso $horarioReceso): JsonResponse
{
return response()->json([
'success' => true,
'data' => $horarioReceso,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, HorarioReceso $horarioReceso): JsonResponse
{
$payload = $request->only((new HorarioReceso())->getFillable());
$horarioReceso->update($payload);
return response()->json([
'success' => true,
'data' => $horarioReceso,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(HorarioReceso $horarioReceso): JsonResponse
{
$horarioReceso->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\LogSeguridad;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class LogSeguridadController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = LogSeguridad::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new LogSeguridad())->getFillable());
$logSeguridad = LogSeguridad::create($payload);
return response()->json([
'success' => true,
'data' => $logSeguridad,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(LogSeguridad $logSeguridad): JsonResponse
{
return response()->json([
'success' => true,
'data' => $logSeguridad,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, LogSeguridad $logSeguridad): JsonResponse
{
$payload = $request->only((new LogSeguridad())->getFillable());
$logSeguridad->update($payload);
return response()->json([
'success' => true,
'data' => $logSeguridad,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(LogSeguridad $logSeguridad): JsonResponse
{
$logSeguridad->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\Modalidad;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ModalidadController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = Modalidad::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new Modalidad())->getFillable());
$modalidad = Modalidad::create($payload);
return response()->json([
'success' => true,
'data' => $modalidad,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(Modalidad $modalidad): JsonResponse
{
return response()->json([
'success' => true,
'data' => $modalidad,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Modalidad $modalidad): JsonResponse
{
$payload = $request->only((new Modalidad())->getFillable());
$modalidad->update($payload);
return response()->json([
'success' => true,
'data' => $modalidad,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Modalidad $modalidad): JsonResponse
{
$modalidad->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\ModoVacaciones;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ModoVacacionesController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = ModoVacaciones::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new ModoVacaciones())->getFillable());
$modoVacaciones = ModoVacaciones::create($payload);
return response()->json([
'success' => true,
'data' => $modoVacaciones,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(ModoVacaciones $modoVacaciones): JsonResponse
{
return response()->json([
'success' => true,
'data' => $modoVacaciones,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, ModoVacaciones $modoVacaciones): JsonResponse
{
$payload = $request->only((new ModoVacaciones())->getFillable());
$modoVacaciones->update($payload);
return response()->json([
'success' => true,
'data' => $modoVacaciones,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(ModoVacaciones $modoVacaciones): JsonResponse
{
$modoVacaciones->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\Persona;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class PersonaController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = Persona::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new Persona())->getFillable());
$persona = Persona::create($payload);
return response()->json([
'success' => true,
'data' => $persona,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(Persona $persona): JsonResponse
{
return response()->json([
'success' => true,
'data' => $persona,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Persona $persona): JsonResponse
{
$payload = $request->only((new Persona())->getFillable());
$persona->update($payload);
return response()->json([
'success' => true,
'data' => $persona,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Persona $persona): JsonResponse
{
$persona->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\Profesion;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ProfesionController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = Profesion::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new Profesion())->getFillable());
$profesion = Profesion::create($payload);
return response()->json([
'success' => true,
'data' => $profesion,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(Profesion $profesion): JsonResponse
{
return response()->json([
'success' => true,
'data' => $profesion,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Profesion $profesion): JsonResponse
{
$payload = $request->only((new Profesion())->getFillable());
$profesion->update($payload);
return response()->json([
'success' => true,
'data' => $profesion,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Profesion $profesion): JsonResponse
{
$profesion->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
@@ -0,0 +1,124 @@
<?php
namespace App\Http\Controllers;
use App\Models\Profesional;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Http\Controllers\LogSeguridadController;
class ProfesionalController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = Profesional::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
// 1. Validamos los datos de entrada
$validated = $request->validate([
'inicio' => 'required|date_format:Y-m-d H:i:s',
'correo' => 'nullable|email|max:255',
'nombrecompleto' => 'nullable|string|max:255',
'descripcion' => 'required|string',
'cliente_id' => 'nullable|exists:clientes,id',
'estadoturno_id' => 'required|exists:estadosturnos,id',
'agenda_id' => 'required|exists:agendas,id',
'profesional_id' => 'required|exists:profesionales,id',
'servicio_id' => 'required|exists:servicios,id',
'modalidad_id' => 'required|exists:modalidades,id',
]);
// 2. Verificamos si el profesional ya tiene un turno en ese "inicio"
$existeTurno = Turno::where('profesional_id', $validated['profesional_id'])
->where('inicio', $validated['inicio'])
->whereIn('estadoturno_id', [1, 2]) // 1: Pendiente, 2: Confirmado
->exists();
if ($existeTurno) {
return response()->json([
'success' => false,
'message' => 'Horario no disponible.',
], 422); // Error de validación lógica
}
$turno = Turno::create($validated);
// 3. Registramos el evento en el log de seguridad
$personaId = auth()->check() ? auth()->user()->persona->id : null;
LogSeguridadController::registrarAccion('Creacion de turno ID: ' . $turno->id, 'Profesional', 17, $personaId);
return response()->json([
'success' => true,
'data' => $turno,
'message' => 'Turno agendado correctamente para el ' . $turno->inicio->format('d/m/Y H:i'),
], 201);
}
/**
* Display the specified resource.
*/
public function show(Profesional $profesional): JsonResponse
{
return response()->json([
'success' => true,
'data' => $profesional,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Profesional $profesional): JsonResponse
{
$payload = $request->only((new Profesional())->getFillable());
$profesional->update($payload);
return response()->json([
'success' => true,
'data' => $profesional,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Profesional $profesional): JsonResponse
{
$profesional->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
public function aceptarFormulario(Formulario $formulario): JsonResponse
{
$turno->update(['estadoturno_id' => 2]);
return response()->json([
'success' => true,
'message' => 'Turno aceptado correctamente',
], 200);
}
}
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\Servicio;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ServicioController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = Servicio::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new Servicio())->getFillable());
$servicio = Servicio::create($payload);
return response()->json([
'success' => true,
'data' => $servicio,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(Servicio $servicio): JsonResponse
{
return response()->json([
'success' => true,
'data' => $servicio,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Servicio $servicio): JsonResponse
{
$payload = $request->only((new Servicio())->getFillable());
$servicio->update($payload);
return response()->json([
'success' => true,
'data' => $servicio,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Servicio $servicio): JsonResponse
{
$servicio->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\Telefono;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class TelefonoController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = Telefono::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new Telefono())->getFillable());
$telefono = Telefono::create($payload);
return response()->json([
'success' => true,
'data' => $telefono,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(Telefono $telefono): JsonResponse
{
return response()->json([
'success' => true,
'data' => $telefono,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Telefono $telefono): JsonResponse
{
$payload = $request->only((new Telefono())->getFillable());
$telefono->update($payload);
return response()->json([
'success' => true,
'data' => $telefono,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Telefono $telefono): JsonResponse
{
$telefono->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
+134
View File
@@ -0,0 +1,134 @@
<?php
namespace App\Http\Controllers;
use App\Models\Turno;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class TurnoController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = Turno::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new Turno())->getFillable());
$turno = Turno::create($payload);
return response()->json([
'success' => true,
'data' => $turno,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(Turno $turno): JsonResponse
{
return response()->json([
'success' => true,
'data' => $turno,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Turno $turno): JsonResponse
{
$payload = $request->only((new Turno())->getFillable());
$turno->update($payload);
return response()->json([
'success' => true,
'data' => $turno,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Turno $turno): JsonResponse
{
$turno->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
public function confirmar(Turno $turno): JsonResponse
{
$turno->confirmar();
return response()->json([
'success' => true,
'data' => $turno,
'message' => 'Turno confirmado correctamente',
], 200);
}
public function cancelar(Turno $turno): JsonResponse
{
$turno->cancelar();
return response()->json([
'success' => true,
'data' => $turno,
'message' => 'Turno cancelado correctamente',
], 200);
}
public function reprogramar(Turno $turno): JsonResponse
{
$turno->reprogramar();
return response()->json([
'success' => true,
'data' => $turno,
'message' => 'Turno reprogramado correctamente',
], 200);
}
public function clienteAusente(Turno $turno): JsonResponse
{
$turno->clienteAusente();
return response()->json([
'success' => true,
'data' => $turno,
'message' => 'Turno marcado como cliente ausente',
], 200);
}
public function clientePresente(Turno $turno): JsonResponse
{
$turno->clientePresente();
return response()->json([
'success' => true,
'data' => $turno,
'message' => 'Turno marcado como cliente presente',
], 200);
}
}
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\Ubicacion;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class UbicacionController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = Ubicacion::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new Ubicacion())->getFillable());
$ubicacion = Ubicacion::create($payload);
return response()->json([
'success' => true,
'data' => $ubicacion,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(Ubicacion $ubicacion): JsonResponse
{
return response()->json([
'success' => true,
'data' => $ubicacion,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Ubicacion $ubicacion): JsonResponse
{
$payload = $request->only((new Ubicacion())->getFillable());
$ubicacion->update($payload);
return response()->json([
'success' => true,
'data' => $ubicacion,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Ubicacion $ubicacion): JsonResponse
{
$ubicacion->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
+79
View File
@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class UserController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): JsonResponse
{
$items = User::all();
return response()->json([
'success' => true,
'data' => $items,
'message' => 'Registros obtenidos correctamente',
], 200);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
$payload = $request->only((new User())->getFillable());
$user = User::create($payload);
return response()->json([
'success' => true,
'data' => $user,
'message' => 'Registro creado correctamente',
], 201);
}
/**
* Display the specified resource.
*/
public function show(User $user): JsonResponse
{
return response()->json([
'success' => true,
'data' => $user,
'message' => 'Registro obtenido correctamente',
], 200);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, User $user): JsonResponse
{
$payload = $request->only((new User())->getFillable());
$user->update($payload);
return response()->json([
'success' => true,
'data' => $user,
'message' => 'Registro actualizado correctamente',
], 200);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(User $user): JsonResponse
{
$user->delete();
return response()->json([
'success' => true,
'message' => 'Registro eliminado correctamente',
], 200);
}
}
+51
View File
@@ -0,0 +1,51 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class SecurityHeaders
{
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
$entornoLocal = app()->environment(['local', 'development']);
$sesionAutenticada = (bool) $request->session()->get('cliente_auth', false)
|| (bool) $request->session()->get('personal_auth', false);
$styleSrc = $entornoLocal
? "style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net http:;"
: "style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net;";
$scriptSrc = $entornoLocal
? "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net http:;"
: "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net;";
$connectSrc = $entornoLocal
? "connect-src 'self' http: https: ws: wss:;"
: "connect-src 'self';";
$response->headers->set('X-Frame-Options', 'SAMEORIGIN');
$response->headers->set('X-Content-Type-Options', 'nosniff');
$response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
$response->headers->set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
$response->headers->set(
'Content-Security-Policy',
"default-src 'self'; base-uri 'self'; frame-ancestors 'self'; form-action 'self'; img-src 'self' data: https:; {$styleSrc} {$scriptSrc} font-src 'self' data: https:; {$connectSrc} frame-src 'self' https://maps.google.com https://www.google.com;"
);
if ($request->isSecure()) {
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
}
if ($sesionAutenticada) {
$response->headers->set('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0, private');
$response->headers->set('Pragma', 'no-cache');
$response->headers->set('Expires', 'Thu, 01 Jan 1970 00:00:00 GMT');
}
return $response;
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class AccionLog extends Model
{
use HasFactory;
protected $table = 'accioneslogs';
protected $fillable = [
'descripcion',
];
//tiene una
public function logSeguridad()
{
return $this->hasMany(LogSeguridad::class, 'accion_id', 'id');
}
}
+30
View File
@@ -0,0 +1,30 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Administrador extends Model
{
use HasFactory;
protected $table = 'administradores';
protected $fillable = [
'persona_id',
'dni',
'correo',
'pregunta_secreta_hash',
'respuesta_secreta_hash',
'credencialprofesional_id',
];
public function credencial()
{
return $this->belongsTo(CredencialProfesional::class, 'credencialprofesional_id','id');
}
public function persona()
{
return $this->belongsTo(Persona::class, 'persona_id','id');
}
}
+549
View File
@@ -0,0 +1,549 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Support\Facades\DB;
class Agenda extends Model
{
use HasFactory;
protected $table = 'agendas';
protected $fillable = [
'estado',
'duracionturno',
'profesional_id',
];
public function profesional()
{
return $this->belongsTo(Profesional::class, 'profesional_id','id');
}
public function diaDeAtencion()
{
return $this->hasMany(DiaDeAtencion::class, 'agenda_id','id');
}
public function turno()
{
return $this->hasMany(Turno::class, 'agenda_id','id');
}
public function feriado()
{
return $this->hasMany(Feriado::class, 'agenda_id','id');
}
public function modoVacaciones()
{
return $this->hasMany(ModoVacaciones::class, 'agenda_id', 'id');
}
public function formularios()
{
return $this->belongsToMany(
\App\Models\Formulario::class,
'profesionales_formularios',
'profesional_id',
'formulario_id',
'profesional_id',
'id'
)->withPivot('estadoformulario')->withTimestamps();
}
public function estaDisponible($fecha, $hora, $agendaId = null) // Verificar si la agenda está disponible para una fecha y hora específicas
{
$agenda = $agendaId ? self::find($agendaId) : $this;
if (!$agenda) {
return false;
}
// Verificar si la fecha es un feriado
if ($agenda->feriado()->where('fecha', $fecha)->exists()) {
return false;
}
// Verificar si la fecha está dentro de un período de vacaciones
if ($agenda->modoVacaciones()->where('inicio', '<=', $fecha)->where('fin', '>=', $fecha)->exists()) {
return false;
}
// Verificar si el día de atención corresponde al día de la semana de la fecha
$diaSemana = date('N', strtotime($fecha)); // 1 (lunes) a 7 (domingo)
$diaAtencion = $agenda->diaDeAtencion()->where('dia_id', $diaSemana)->first();
if (!$diaAtencion) {
return false;
}
// Verificar si la hora está dentro del horario de atención
if (!$diaAtencion->horariosAtenciones()->where('horariocomienzo', '<=', $hora)->where('horariofin', '>=', $hora)->exists()) {
return false;
}
// Verificar si la hora no está dentro de un horario de receso
if ($diaAtencion->horariosRecesos()->where('comienzo', '<=', $hora)->where('fin', '>=', $hora)->exists()) {
return false;
}
// Verificar si ya existe un turno para esa fecha y hora
if ($agenda->turno()->where('inicio', $fecha . ' ' . $hora)->exists()) {
return false;
}
return true;
}
public function obtenerTurnoDisponible($idProfesional, $tipopreferencia = 'INDISTINTO', $diasPreferencia = []) //Devuelve el turno disponible más cercano según preferencias
{
// Inicializa estructuras de salida con valores por defecto.
$DiasDeAtenciones = array_fill(0, 7, array_fill(0, 5, null));
$tipo = null;
$recesos = [];
$vacaciones = [];
$feriados = [];
$turnoMasCercano = null;
// Busca la agenda asociada al profesional recibido por parámetro.
$agendaId = self::where('profesional_id', $idProfesional)->value('id');
// Si no existe agenda para ese profesional, devuelve la estructura vacía.
if (!$agendaId) {
return null;
}
// Obtiene días de atención y sus horarios AM/PM para la agenda encontrada.
// [fila][0]=dia_id, [fila][1]=inicio AM, [fila][2]=fin AM, [fila][3]=inicio PM, [fila][4]=fin PM
$filasAtencion = DB::table('diasdeatenciones as d')
->leftJoin('horariosatenciones as h', 'h.diadeatencion_id', '=', 'd.id')
->where('d.agenda_id', $agendaId)
->select('d.dia_id', 'h.horariocomienzo', 'h.horariofin', 'h.tipo')
->orderBy('d.dia_id')
->get();
// Recorre cada fila y la ubica en la matriz de 7x5 según día y tipo de horario.
foreach ($filasAtencion as $filaAtencion) {
$fila = (int) $filaAtencion->dia_id - 1;
// Ignora valores fuera del rango esperado de días (1 a 7).
if ($fila < 0 || $fila > 6) {
continue;
}
// Guarda el identificador del día en la primera columna.
$DiasDeAtenciones[$fila][0] = $filaAtencion->dia_id;
// Si no hay tipo de horario, salta al siguiente registro.
if ($filaAtencion->tipo === null) {
continue;
}
// Normaliza y guarda el tipo actual (AM o PM).
$tipoActual = strtoupper(trim((string) $filaAtencion->tipo));
$tipo = $tipoActual;
// Completa columnas de mañana (inicio y fin).
if ($tipoActual === 'AM') {
$DiasDeAtenciones[$fila][1] = $filaAtencion->horariocomienzo;
$DiasDeAtenciones[$fila][2] = $filaAtencion->horariofin;
}
// Completa columnas de tarde (inicio y fin).
if ($tipoActual === 'PM') {
$DiasDeAtenciones[$fila][3] = $filaAtencion->horariocomienzo;
$DiasDeAtenciones[$fila][4] = $filaAtencion->horariofin;
}
}
// Trae los recesos de la agenda y los transforma a una matriz [dia_id, comienzo, fin].
$recesos = DB::table('horariosrecesos as r')
->join('diasdeatenciones as d', 'd.id', '=', 'r.diadeatencion_id')
->where('d.agenda_id', $agendaId)
->select('d.dia_id', 'r.comienzo', 'r.fin')
->orderBy('d.dia_id')
->get()
->map(function ($receso) {
return [
$receso->dia_id,
$receso->comienzo,
$receso->fin,
];
})
->values()
->all();
// Trae períodos de vacaciones y los transforma a una matriz [inicio, fin].
$vacaciones = DB::table('modosvacaciones')
->where('agenda_id', $agendaId)
->select('inicio', 'fin')
->orderBy('inicio')
->get()
->map(function ($vacacion) {
return [
$vacacion->inicio,
$vacacion->fin,
];
})
->values()
->all();
// Trae todos los feriados de la agenda y los transforma en una matriz de fechas.
$feriados = DB::table('feriados')
->where('agenda_id', $agendaId)
->select('fecha')
->orderBy('fecha')
->pluck('fecha')
->values()
->all();
// Normaliza preferencia horaria (AM, PM o INDISTINTO).
$tipopreferencia = strtoupper(trim((string) $tipopreferencia));
if (!in_array($tipopreferencia, ['AM', 'PM', 'INDISTINTO'], true)) {
$tipopreferencia = 'INDISTINTO';
}
// Convierte los días preferidos en texto a ids de 1 (lunes) a 7 (domingo).
$mapaDias = [
'lunes' => 1,
'martes' => 2,
'miercoles' => 3,
'jueves' => 4,
'viernes' => 5,
'sabado' => 6,
'domingo' => 7,
];
$diasPreferidosIds = [];
foreach ((array) $diasPreferencia as $diaPreferido) {
$diaNormalizado = strtolower(trim((string) $diaPreferido));
$diaNormalizado = strtr($diaNormalizado, [
'á' => 'a',
'é' => 'e',
'í' => 'i',
'ó' => 'o',
'ú' => 'u',
]);
if (isset($mapaDias[$diaNormalizado])) {
$diasPreferidosIds[] = $mapaDias[$diaNormalizado];
}
}
$diasPreferidosIds = array_values(array_unique($diasPreferidosIds));
// Toma la duración de turno de la agenda; si no existe, usa 30 minutos.
$duracionTurno = (int) (self::where('id', $agendaId)->value('duracionturno') ?? 30);
if ($duracionTurno <= 0) {
$duracionTurno = 30;
}
// Define el punto de inicio de la búsqueda desde el día siguiente (para evitar asignar turnos inmediatos del día actual).
$baseTimestamp = strtotime('tomorrow');
// Crea índices rápidos para validar fechas bloqueadas y horarios ocupados.
$feriadosLookup = array_flip($feriados);
$recesosPorDia = [];
foreach ($recesos as $receso) {
$diaId = (int) $receso[0];
if (!isset($recesosPorDia[$diaId])) {
$recesosPorDia[$diaId] = [];
}
$recesosPorDia[$diaId][] = [$receso[1], $receso[2]];
}
$turnosOcupadosLookup = [];
$turnosOcupados = DB::table('turnos')
->where('agenda_id', $agendaId)
->where('inicio', '>=', date('Y-m-d', $baseTimestamp))
->select('inicio')
->get();
foreach ($turnosOcupados as $turnoOcupado) {
$tsTurno = strtotime((string) $turnoOcupado->inicio);
if ($tsTurno === false) {
continue;
}
$turnosOcupadosLookup[date('Y-m-d H:i:s', $tsTurno)] = true;
}
// Busca el primer turno disponible respetando tipo, días preferidos y reglas de agenda.
for ($offsetDias = 0; $offsetDias <= 120; $offsetDias++) {
$tsDia = strtotime(date('Y-m-d', $baseTimestamp) . ' +' . $offsetDias . ' day');
if ($tsDia === false) {
continue;
}
$fechaIterada = date('Y-m-d', $tsDia);
$diaId = (int) date('N', $tsDia);
if (!empty($diasPreferidosIds) && !in_array($diaId, $diasPreferidosIds, true)) {
continue;
}
if (isset($feriadosLookup[$fechaIterada])) {
continue;
}
$enVacaciones = false;
foreach ($vacaciones as $vacacion) {
if ($fechaIterada >= $vacacion[0] && $fechaIterada <= $vacacion[1]) {
$enVacaciones = true;
break;
}
}
if ($enVacaciones) {
continue;
}
$filaDia = $DiasDeAtenciones[$diaId - 1] ?? null;
if (!$filaDia || $filaDia[0] === null) {
continue;
}
$ventanas = [];
if (($tipopreferencia === 'AM' || $tipopreferencia === 'INDISTINTO') && $filaDia[1] !== null && $filaDia[2] !== null) {
$ventanas[] = ['tipo' => 'AM', 'inicio' => $filaDia[1], 'fin' => $filaDia[2]];
}
if (($tipopreferencia === 'PM' || $tipopreferencia === 'INDISTINTO') && $filaDia[3] !== null && $filaDia[4] !== null) {
$ventanas[] = ['tipo' => 'PM', 'inicio' => $filaDia[3], 'fin' => $filaDia[4]];
}
foreach ($ventanas as $ventana) {
$inicioVentanaTs = strtotime($fechaIterada . ' ' . $ventana['inicio']);
$finVentanaTs = strtotime($fechaIterada . ' ' . $ventana['fin']);
if ($inicioVentanaTs === false || $finVentanaTs === false || $inicioVentanaTs >= $finVentanaTs) {
continue;
}
for ($slotTs = $inicioVentanaTs; ($slotTs + ($duracionTurno * 60)) <= $finVentanaTs; $slotTs += ($duracionTurno * 60)) {
$slotFecha = date('Y-m-d', $slotTs);
$slotHora = date('H:i:s', $slotTs);
$slotDateTime = $slotFecha . ' ' . $slotHora;
$slotFinTs = $slotTs + ($duracionTurno * 60);
if (isset($turnosOcupadosLookup[$slotDateTime])) {
continue;
}
$solapaReceso = false;
foreach ($recesosPorDia[$diaId] ?? [] as $recesoDia) {
$recesoInicioTs = strtotime($slotFecha . ' ' . $recesoDia[0]);
$recesoFinTs = strtotime($slotFecha . ' ' . $recesoDia[1]);
if ($recesoInicioTs === false || $recesoFinTs === false) {
continue;
}
if ($slotTs < $recesoFinTs && $slotFinTs > $recesoInicioTs) {
$solapaReceso = true;
break;
}
}
if ($solapaReceso) {
continue;
}
if (!$this->estaDisponible($slotFecha, $slotHora, $agendaId)) {
continue;
}
$turnoMasCercano = [
'fecha' => $slotFecha,
'hora' => $slotHora,
'fechaHora' => $slotDateTime,
'tipo' => $ventana['tipo'],
'duracionMinutos' => $duracionTurno,
];
break 3;
}
}
}
// Devuelve solo la fecha/hora del turno más cercano o null si no hay disponibilidad.
return $turnoMasCercano['fechaHora'] ?? null;
}
public function crearDiaDeAtencion($diaId, $horarioComienzo, $horarioFin, $tipo)
{
$tipoNormalizado = strtoupper(trim((string) $tipo));
if (!in_array($tipoNormalizado, ['AM', 'PM'], true)) {
throw new \InvalidArgumentException('El tipo debe ser AM o PM.');
}
$horarioComienzoNormalizado = $this->normalizarHora($horarioComienzo, 'horarioComienzo');
$horarioFinNormalizado = $this->normalizarHora($horarioFin, 'horarioFin');
if (strtotime('1970-01-01 ' . $horarioComienzoNormalizado) >= strtotime('1970-01-01 ' . $horarioFinNormalizado)) {
throw new \InvalidArgumentException('horarioComienzo debe ser menor que horarioFin.');
}
return DB::transaction(function () use ($diaId, $tipoNormalizado, $horarioComienzoNormalizado, $horarioFinNormalizado) {
$diaAtencion = DiaDeAtencion::firstOrCreate(
['agenda_id' => $this->id, 'dia_id' => $diaId],
['descripcion' => 'Dia de atencion']
);
HorarioDeAtencion::updateOrCreate(
[
'diadeatencion_id' => $diaAtencion->id,
'tipo' => $tipoNormalizado,
],
[
'horariocomienzo' => $horarioComienzoNormalizado,
'horariofin' => $horarioFinNormalizado,
]
);
return $diaAtencion;
});
}
public function eliminarDiaDeAtencion($diaId)
{
return DB::transaction(function () use ($diaId) {
$diaAtencion = DiaDeAtencion::where('agenda_id', $this->id)->where('dia_id', $diaId)->first();
if ($diaAtencion) {
HorarioDeAtencion::where('diadeatencion_id', $diaAtencion->id)->delete();
HorarioReceso::where('diadeatencion_id', $diaAtencion->id)->delete();
$diaAtencion->delete();
}
});
}
public function crearModoVacaciones($inicio, $fin, $descripcion = null)
{
return ModoVacaciones::create([
'agenda_id' => $this->id,
'inicio' => $inicio,
'fin' => $fin,
'descripcion' => $descripcion,
]);
}
public function eliminarModoVacaciones($id)
{
$modoVacaciones = ModoVacaciones::where('agenda_id', $this->id)->where('id', $id)->first();
if ($modoVacaciones) {
$modoVacaciones->delete();
}
}
public function crearFeriado($fecha, $descripcion = null)
{
return Feriado::create([
'agenda_id' => $this->id,
'fecha' => $fecha,
'descripcion' => $descripcion,
]);
}
public function eliminarFeriado($id)
{
$feriado = Feriado::where('agenda_id', $this->id)->where('id', $id)->first();
if ($feriado) {
$feriado->delete();
}
}
public function guardarTurno($inicio, $correo = null, $nombrecompleto = null, $descripcion = null, $clienteId = null, $estadoturnoId = null, $profesionalId = null, $servicioId = null, $modalidadId = null, $agendaId = null)
{
$inicioNormalizado = $this->normalizarDatetime($inicio, 'inicio');
$turno = Turno::create([
'agenda_id' => $agendaId ?? $this->id,
'correo' => $correo,
'nombrecompleto' => $nombrecompleto,
'descripcion' => $descripcion,
'cliente_id' => $clienteId,
'estadoturno_id' => $estadoturnoId,
'profesional_id' => $profesionalId,
'servicio_id' => $servicioId,
'modalidad_id' => $modalidadId,
'inicio' => $inicioNormalizado,
]);
return $turno;
}
private function normalizarHora($valor, $campo)
{
if ($valor instanceof \DateTimeInterface) {
return $valor->format('H:i:s');
}
$timestamp = strtotime(trim((string) $valor));
if ($timestamp === false) {
throw new \InvalidArgumentException('El campo ' . $campo . ' debe ser una hora valida.');
}
return date('H:i:s', $timestamp);
}
private function normalizarDatetime($valor, $campo)
{
if ($valor instanceof \DateTimeInterface) {
return $valor->format('Y-m-d H:i:s');
}
$timestamp = strtotime(trim((string) $valor));
if ($timestamp === false) {
throw new \InvalidArgumentException('El campo ' . $campo . ' debe ser una fecha y hora valida.');
}
return date('Y-m-d H:i:s', $timestamp);
}
public function confirmarTurno($id)
{
$turno = $this->turno()->find($id);
if (!$turno) {
return null;
}
return $turno->confirmar();
}
public function cancelarTurno($id)
{
$turno = $this->turno()->find($id);
if (!$turno) {
return null;
}
return $turno->cancelar();
}
public function reprogramarTurno($id)
{
$turno = $this->turno()->find($id);
if (!$turno) {
return null;
}
return $turno->reprogramar();
}
public function marcarClienteAusente($id)
{
$turno = $this->turno()->find($id);
if (!$turno) {
return null;
}
return $turno->clienteAusente();
}
public function marcarClientePresente($id)
{
$turno = $this->turno()->find($id);
if (!$turno) {
return null;
}
return $turno->clientePresente();
}
}
+22
View File
@@ -0,0 +1,22 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class AsistenteSinRespuesta extends Model
{
public $timestamps = false;
protected $table = 'asistente_sin_respuesta';
protected $fillable = [
'consulta',
'revisado',
];
protected $casts = [
'revisado' => 'boolean',
'created_at' => 'datetime',
];
}
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Baja extends Model
{
use HasFactory;
protected $table = 'bajas';
protected $fillable = [
'descripcion',
];
//tiene una
public function cliente()
{
return $this->hasOne(Cliente::class, 'baja_id', 'id');
}
public function profesional()
{
return $this->hasOne(Profesional::class, 'baja_id', 'id');
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Bug extends Model
{
use HasFactory;
protected $table = 'bugs';
protected $fillable = [
'titulo',
'descripcion',
'prioridad',
'estado',
'version',
'fotobug_id',
];
public function fotoBug()
{
return $this->belongsTo(FotoBug::class, 'fotobug_id', 'id');
}
}
+57
View File
@@ -0,0 +1,57 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Cliente extends Model
{
use HasFactory;
protected $table = 'clientes';
protected $fillable = [
'dni',
'correo',
'persona_id',
'baja_id',
'credencialcliente_id',
];
public function persona()
{
return $this->belongsTo(Persona::class, 'persona_id','id');
}
public function baja()
{
return $this->belongsTo(Baja::class, 'baja_id','id');
}
public function credencialCliente()
{
return $this->belongsTo(CredencialCliente::class, 'credencialcliente_id','id');
}
public function documentosCliente()
{
return $this->hasMany(DocumentacionCliente::class, 'cliente_id','id');
}
public function turnos()
{
return $this->hasMany(Turno::class, 'cliente_id','id');
}
public function profesionales()
{
return $this->belongsToMany(Profesional::class, 'profesionales_clientes','cliente_id', 'profesional_id')
->withPivot('estadorelacion')
->withTimestamps();
}
public function formularios()
{
return $this->hasMany(Formulario::class, 'cliente_id','id');
}
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class ContenidoWeb extends Model
{
use HasFactory;
protected $table = 'contenidoswebs';
protected $fillable = [
'quienessomos',
];
public function ubicaciones()
{
return $this->hasMany(Ubicacion::class, 'contenidoweb_id', 'id');
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class CredencialCliente extends Model
{
use HasFactory;
protected $table = 'credencialesclientes';
protected $fillable = [
'contra',
'correo',
'token',
'fecha_hora',
'reset_token',
'reset_expira_en',
];
//tiene un
public function cliente()
{
return $this->hasOne(Cliente::class, 'credencialcliente_id', 'id');
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class CredencialProfesional extends Model
{
use HasFactory;
protected $table = 'credencialesprofesionales';
protected $fillable = [
'usuario',
'contra',
'rol',
'token',
'fecha_hora',
'reset_token',
'reset_expira_en',
];
public function profesional()
{
return $this->hasOne(Profesional::class, 'credencialprofesional_id', 'id');
}
public function administrador()
{
return $this->hasOne(Administrador::class, 'credencialprofesional_id', 'id');
}
}
+23
View File
@@ -0,0 +1,23 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Dia extends Model
{
use HasFactory;
protected $table = 'dias';
protected $fillable = [
'descripcion',
];
public function diaDeAtencion()
{
return $this->hasMany(DiaDeAtencion::class, 'dia_id', 'id');
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class DiaDeAtencion extends Model
{
use HasFactory;
protected $table = 'diasdeatenciones';
protected $fillable = [
'agenda_id',
'dia_id',
];
public function agenda()
{
return $this->belongsTo(Agenda::class, 'agenda_id', 'id');
}
public function dias()
{
return $this->belongsTo(Dia::class, 'dia_id', 'id');
}
public function horariosRecesos()
{
return $this->hasMany(HorarioReceso::class, 'diadeatencion_id', 'id');
}
public function horariosAtenciones()
{
return $this->hasMany(HorarioDeAtencion::class, 'diadeatencion_id', 'id');
}
}
+23
View File
@@ -0,0 +1,23 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class DiaPreferencia extends Model
{
use HasFactory;
protected $table = 'diaspreferencias';
protected $fillable = [
'descripcion',
'formulario_id',
];
public function formularios()
{
return $this->belongsToMany(Formulario::class, 'formularios_diaspreferidos', 'diapreferencia_id', 'formulario_id');
}
}
+31
View File
@@ -0,0 +1,31 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class DocumentacionCliente extends Model
{
use HasFactory;
protected $table = 'documentacionesclientes';
protected $fillable=[
'nombre',
'mime_type',
'tamanio_bytes',
'extension',
'cliente_id',
'profesional_id',
];
public function cliente()
{
return $this->belongsTo(Cliente::class, 'cliente_id','id');
}
public function profesional()
{
return $this->belongsTo(Profesional::class, 'profesional_id','id');
}
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Error extends Model
{
use HasFactory;
protected $table = 'errores';
protected $fillable = [
'codigo',
'mensaje',
'track_trace',
'url',
'fecha_hora',
];
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class EstadoTurno extends Model
{
use HasFactory;
protected $table = 'estadosturnos';
protected $fillable = [
'descripcion',
];
public function turno()
{
return $this->hasMany(Turno::class, 'estadoturno_id', 'id');
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class FaqAsistente extends Model
{
use HasFactory;
protected $table = 'faq_asistentes';
protected $fillable = [
'intencion',
'palabras_clave',
'respuesta',
'orden',
'activo',
];
protected $casts = [
'palabras_clave' => 'array',
'activo' => 'boolean',
];
}
+23
View File
@@ -0,0 +1,23 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Feriado extends Model
{
use HasFactory;
protected $table = 'feriados';
protected $fillable = [
'fecha',
'descripcion',
'agenda_id',
];
public function agenda()
{
return $this->belongsTo(Agenda::class, 'agenda_id', 'id');
}
}
+65
View File
@@ -0,0 +1,65 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Formulario extends Model
{
use HasFactory;
protected $table = 'formularios';
protected $fillable = [
'descripcion',
'nombrecompleto',
'correo',
'celular',
'ip_origen',
'estado',
'profesion_id',
'servicio_id',
'modalidad_id',
'profesional_id',
'cliente_id',
'fechaenvio',
];
public function modalidad()
{
return $this->belongsTo(Modalidad::class, 'modalidad_id', 'id');
}
public function profesion()
{
return $this->belongsTo(Profesion::class, 'profesion_id', 'id');
}
public function servicio()
{
return $this->belongsTo(Servicio::class, 'servicio_id', 'id');
}
public function cliente()
{
return $this->belongsTo(Cliente::class, 'cliente_id', 'id');
}
public function profesional()
{
return $this->belongsToMany(Profesional::class, 'profesionales_formularios', 'formulario_id', 'profesional_id')
->withPivot('estadoformulario')
->withTimestamps();
}
public function diasPreferidos()
{
return $this->belongsToMany(DiaPreferencia::class, 'formularios_diaspreferidos', 'formulario_id', 'diapreferencia_id');
}
public function horariosPreferidos()
{
return $this->belongsToMany(HorarioPreferencia::class, 'formularios_horariospreferidos', 'formulario_id', 'horariopreferencia_id');
}
}
+31
View File
@@ -0,0 +1,31 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Foto extends Model
{
use HasFactory;
protected $table = 'fotos';
protected $fillable = [
'extension',
'tamanio_bytes',
'nombre',
'mime_type',
'ruta',
];
public function persona()
{
return $this->hasMany(Persona::class, 'foto_id', 'id');
}
public function servicio()
{
return $this->hasMany(Servicio::class, 'foto_id', 'id');
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class FotoBug extends Model
{
use HasFactory;
protected $table = 'fotosbugs';
protected $fillable = [
'extension',
'tamanio_bytes',
'nombre',
'mime_type',
];
public function bug()
{
return $this->hasOne(Bug::class, 'fotobug_id', 'id');
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class HorarioDeAtencion extends Model
{
use HasFactory;
protected $table = 'horariosatenciones';
protected $fillable = [
'horariocomienzo',
'horariofin',
'tipo',
'diadeatencion_id',
];
public function diaatencion()
{
return $this->belongsTo(DiaDeAtencion::class, 'diadeatencion_id', 'id');
}
}
+22
View File
@@ -0,0 +1,22 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class HorarioPreferencia extends Model
{
use HasFactory;
protected $table = 'horariospreferencias';
protected $fillable = [
'descripcion',
];
public function formularios()
{
return $this->belongsToMany(Formulario::class, 'formularios_horariospreferidos', 'horariopreferencia_id', 'formulario_id');
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class HorarioReceso extends Model
{
use HasFactory;
protected $table = 'horariosrecesos';
protected $fillable = [
'comienzo',
'fin',
'diadeatencion_id',
];
public function diaDeAtencion()
{
return $this->belongsTo(DiaDeAtencion::class, 'diadeatencion_id', 'id');
}
}
+37
View File
@@ -0,0 +1,37 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class LogSeguridad extends Model
{
use HasFactory;
protected $table = 'logseguridades';
public $timestamps = false;
protected $fillable = [
'descripcion',
'fechahora',
'IPorigen',
'rol',
'persona_id',
'accion_id',
'accion_descripcion',
'responsable_nombre',
];
//pertenece a
public function accion()
{
return $this->belongsTo(AccionLog::class, 'accion_id', 'id');
}
public function responsable()
{
return $this->belongsTo(Persona::class, 'persona_id', 'id');
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Modalidad extends Model
{
use HasFactory;
protected $table = 'modalidades';
protected $fillable = [
'descripcion',
];
public function formularios()
{
return $this->hasMany(Formulario::class, 'modalidad_id', 'id');
}
public function turnos()
{
return $this->hasMany(Turno::class, 'modalidad_id', 'id');
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class ModoVacaciones extends Model
{
use HasFactory;
protected $table = 'modosvacaciones';
protected $fillable = [
'inicio',
'fin',
'descripcion',
'agenda_id',
];
public function agenda()
{
return $this->belongsTo(Agenda::class, 'agenda_id', 'id');
}
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Notificacion extends Model
{
use HasFactory;
protected $table = 'notificaciones';
protected $primaryKey = 'notificacion_id';
public $timestamps = false;
protected $fillable = [
'tipo',
'mensaje_inicio',
'mensaje_final',
];
}
+42
View File
@@ -0,0 +1,42 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Persona extends Model
{
use HasFactory;
protected $table = 'personas';
protected $fillable = [
'dni',
'nombre',
'apellido',
'cuil',
'fechanac',
'foto_id'
];
public function Foto()
{
return $this->belongsTo(Foto::class, 'foto_id', 'id');
}
public function profesionales()
{
return $this->hasMany(Profesional::class, 'persona_id');
}
public function cliente()
{
return $this->hasOne(Cliente::class, 'persona_id', 'id');
}
public function telefonos()
{
return $this->belongsToMany(Telefono::class, 'personas_telefonos', 'persona_id', 'telefono_id');
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Profesion extends Model
{
use HasFactory;
protected $table = 'profesiones';
protected $fillable =[
'titulo',
'visible_en_formulario',
];
public function profesionales()
{
return $this->hasMany(Profesional::class, 'profesion_id', 'id');
}
public function profesionalesAsociados()
{
return $this->belongsToMany(Profesional::class, 'profesionales_profesiones', 'profesion_id', 'profesional_id');
}
}
+93
View File
@@ -0,0 +1,93 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Profesional extends Model
{
use HasFactory;
protected $table = 'profesionales';
protected $fillable = [
'matricula',
'correo',
'dni',
'credencialprofesional_id',
'persona_id',
'baja_id',
'profesion_id',
];
//Pertenece a
public function profesion()
{
return $this->belongsTo(Profesion::class, 'profesion_id', 'id');
}
public function profesiones()
{
return $this->belongsToMany(Profesion::class, 'profesionales_profesiones', 'profesional_id', 'profesion_id');
}
public function credencialProfesional()
{
return $this->belongsTo(CredencialProfesional::class, 'credencialprofesional_id', 'id');
}
public function persona()
{
return $this->belongsTo(Persona::class, 'persona_id');
}
public function baja()
{
return $this->belongsTo(Baja::class, 'baja_id');
}
//Tiene una
public function agenda()
{
return $this->hasOne(Agenda::class, 'profesional_id');
}
public function turnos()
{
return $this->hasMany(Turno::class, 'profesional_id');
}
public function documentacionesClientes()
{
return $this->hasMany(DocumentacionCliente::class, 'profesional_id');
}
//tablas intermedias
public function formularios()
{
return $this->belongsToMany(Formulario::class, 'profesionales_formularios', 'profesional_id', 'formulario_id')
->withPivot('estadoformulario')
->withTimestamps();
}
public function servicios()
{
return $this->belongsToMany(Servicio::class, 'profesionales_servicios', 'profesional_id', 'servicio_id');
}
public function clientes()
{
return $this->belongsToMany(Cliente::class, 'profesionales_clientes', 'profesional_id', 'cliente_id')
->withPivot('estadorelacion')
->withTimestamps();
}
}
+59
View File
@@ -0,0 +1,59 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Servicio extends Model
{
use HasFactory;
protected $table = 'servicios';
protected $fillable = [
'titulo',
'estado',
'descripcion',
'visibleenweb',
'contenidoweb_id',
'profesion_id',
'foto_id',
];
//pertenece a
public function contenidoWeb()
{
return $this->belongsTo(ContenidoWeb::class, 'contenidoweb_id', 'id');
}
public function profesion()
{
return $this->belongsTo(Profesion::class, 'profesion_id', 'id');
}
public function foto()
{
return $this->belongsTo(Foto::class, 'foto_id', 'id');
}
// Tiene un
public function formulario()
{
return $this->hasOne(Formulario::class, 'servicio_id', 'id');
}
public function turno()
{
return $this->hasOne(Turno::class, 'servicio_id', 'id');
}
//Tablas intermedias
public function profesional()
{
return $this->belongsToMany(Profesional::class, 'profesionales_servicios', 'servicio_id', 'profesional_id');
}
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Telefono extends Model
{
use HasFactory;
protected $table = 'telefonos';
protected $fillable = [
'telefono',
];
public function persona()
{
return $this->belongsToMany(Persona::class, 'personas_telefonos', 'telefono_id', 'persona_id');
}
}
+95
View File
@@ -0,0 +1,95 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Turno extends Model
{
use HasFactory;
protected $table = 'turnos';
protected $fillable = [
'inicio',
'correo',
'celular',
'nombrecompleto',
'descripcion',
'cliente_id',
'estadoturno_id',
'agenda_id',
'profesional_id',
'servicio_id',
'modalidad_id',
];
protected $casts = [
'inicio' => 'datetime',
];
public function estadoTurno()
{
return $this->belongsTo(EstadoTurno::class, 'estadoturno_id', 'id');
}
public function cliente()
{
return $this->belongsTo(Cliente::class, 'cliente_id', 'id');
}
public function agenda()
{
return $this->belongsTo(Agenda::class, 'agenda_id', 'id');
}
public function profesional()
{
return $this->belongsTo(Profesional::class, 'profesional_id', 'id');
}
public function servicio()
{
return $this->belongsTo(Servicio::class, 'servicio_id', 'id');
}
public function modalidad()
{
return $this->belongsTo(Modalidad::class, 'modalidad_id', 'id');
}
public function confirmar()
{
$this->estadoturno_id = 1; // ID del estado Confirmado (dato maestro)
$this->save();
return $this;
}
public function cancelar()
{
$this->estadoturno_id = 2; // ID del estado Cancelado (dato maestro)
$this->save();
return $this;
}
public function reprogramar()
{
$this->estadoturno_id = 3; // ID del estado Reprogramado (dato maestro)
$this->save();
return $this;
}
public function clienteAusente()
{
$this->estadoturno_id = 4; // ID del estado Cliente Ausente (dato maestro)
$this->save();
return $this;
}
public function clientePresente()
{
$this->estadoturno_id = 5; // ID del estado Cliente Presente (dato maestro)
$this->save();
return $this;
}
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Ubicacion extends Model
{
use HasFactory;
protected $table = 'ubicaciones';
protected $fillable = [
'link',
];
public function contenidoweb()
{
return $this->belongsTo(ContenidoWeb::class, 'contenidoweb_id','id');
}
}
+110 -1
View File
@@ -2,7 +2,15 @@
namespace App\Providers; namespace App\Providers;
use App\Models\Bug;
use App\Models\Profesional;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\View;
class AppServiceProvider extends ServiceProvider class AppServiceProvider extends ServiceProvider
{ {
@@ -19,6 +27,107 @@ class AppServiceProvider extends ServiceProvider
*/ */
public function boot(): void public function boot(): void
{ {
// Paginator::useBootstrapFive();
RateLimiter::for('login-cliente-web', fn (Request $request) => Limit::perHour(5)->by($request->ip()));
RateLimiter::for('login-personal-web', fn (Request $request) => Limit::perHour(5)->by($request->ip()));
RateLimiter::for('login-cliente-api', fn (Request $request) => Limit::perHour(5)->by($request->ip()));
RateLimiter::for('login-personal-api', fn (Request $request) => Limit::perHour(5)->by($request->ip()));
RateLimiter::for('login-api-general', fn (Request $request) => Limit::perHour(5)->by($request->ip()));
RateLimiter::for('recuperar-cliente', fn (Request $request) => Limit::perHour(5)->by($request->ip()));
RateLimiter::for('recuperar-personal', fn (Request $request) => Limit::perHour(5)->by($request->ip()));
RateLimiter::for('recuperar-admin', fn (Request $request) => Limit::perHour(5)->by($request->ip()));
RateLimiter::for('recuperar-admin-pregunta', fn (Request $request) => Limit::perHour(5)->by($request->ip()));
RateLimiter::for('reportar-bugs', fn (Request $request) => Limit::perHour(5)->by($request->ip()));
RateLimiter::for('asistente-chat', fn (Request $request) => Limit::perMinute(20)->by($request->ip()));
View::composer('profesional.*', function ($view): void {
$credencialId = (int) session('personal_credencial_id', 0);
$profesionalId = 0;
if ($credencialId > 0) {
$profesionalId = (int) Profesional::query()
->where('credencialprofesional_id', $credencialId)
->value('id');
}
$formulariosPendientesCount = 0;
if ($profesionalId > 0) {
$formulariosPendientesCount = DB::table('profesionales_formularios as pf')
->join('formularios as f', 'f.id', '=', 'pf.formulario_id')
->where('pf.profesional_id', $profesionalId)
->whereRaw("LOWER(TRIM(pf.estado)) = 'pendiente'")
->whereRaw("LOWER(TRIM(f.estado)) = 'pendiente'")
->count();
}
$view->with('formulariosPendientesCount', $formulariosPendientesCount);
$notificacionesClaves = [];
if ($profesionalId > 0) {
$hoy = now()->toDateString();
$manana = now()->addDay()->toDateString();
$turnosHoy = DB::table('turnos as t')
->where('t.profesional_id', $profesionalId)
->whereDate('t.inicio', $hoy)
->select('t.nombrecompleto', 't.inicio')
->get();
foreach ($turnosHoy as $turno) {
$nombre = trim((string) ($turno->nombrecompleto ?? 'Sin nombre'));
$hora = $turno->inicio ? \Illuminate\Support\Carbon::parse((string) $turno->inicio)->format('H:i') : '-';
$titulo = 'Turno hoy a las ' . $hora . ' — ' . $nombre;
$fecha = 'Hoy ' . $hora;
$notificacionesClaves[] = base64_encode('turno_hoy|' . $titulo . '|' . $fecha);
}
$turnosManana = DB::table('turnos as t')
->where('t.profesional_id', $profesionalId)
->whereDate('t.inicio', $manana)
->select('t.nombrecompleto', 't.inicio')
->get();
foreach ($turnosManana as $turno) {
$nombre = trim((string) ($turno->nombrecompleto ?? 'Sin nombre'));
$hora = $turno->inicio ? \Illuminate\Support\Carbon::parse((string) $turno->inicio)->format('H:i') : '-';
$titulo = 'Turno mañana a las ' . $hora . ' — ' . $nombre;
$fecha = 'Mañana ' . $hora;
$notificacionesClaves[] = base64_encode('turno_manana|' . $titulo . '|' . $fecha);
}
$estadoCanceladoId = (int) DB::table('estadosturnos')->whereRaw('LOWER(TRIM(descripcion)) = ?', ['cancelado'])->value('id');
if ($estadoCanceladoId > 0) {
$turnosCancelados = DB::table('turnos as t')
->where('t.profesional_id', $profesionalId)
->where('t.estadoturno_id', $estadoCanceladoId)
->where('t.updated_at', '>=', now()->subDays(7))
->select('t.nombrecompleto', 't.inicio', 't.updated_at')
->get();
foreach ($turnosCancelados as $turno) {
$nombre = trim((string) ($turno->nombrecompleto ?? 'Sin nombre'));
$fecha = $turno->updated_at
? \Illuminate\Support\Carbon::parse((string) $turno->updated_at)->format('d/m/Y')
: '-';
$titulo = 'Turno cancelado — ' . $nombre;
$notificacionesClaves[] = base64_encode('turno_cancelado|' . $titulo . '|' . $fecha);
}
}
}
$notificacionesCount = count($notificacionesClaves);
$view->with('notificacionesCount', $notificacionesCount);
$view->with('notificacionesClaves', $notificacionesClaves);
});
View::composer('administrador.*', function ($view): void {
$bugsPendientesCount = Bug::query()
->whereRaw("LOWER(TRIM(estado)) = 'pendiente'")
->count();
$view->with('bugsPendientesCount', $bugsPendientesCount);
});
} }
} }
+97 -2
View File
@@ -1,8 +1,15 @@
<?php <?php
use App\Models\Error;
use Illuminate\Foundation\Application; use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions; use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware; use Illuminate\Foundation\Configuration\Middleware;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Http\Exceptions\ThrottleRequestsException;
use Illuminate\Http\Request;
use Illuminate\Session\TokenMismatchException;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
return Application::configure(basePath: dirname(__DIR__)) return Application::configure(basePath: dirname(__DIR__))
->withRouting( ->withRouting(
@@ -11,8 +18,96 @@ return Application::configure(basePath: dirname(__DIR__))
health: '/up', health: '/up',
) )
->withMiddleware(function (Middleware $middleware): void { ->withMiddleware(function (Middleware $middleware): void {
// $middleware->append(\App\Http\Middleware\SecurityHeaders::class);
}) })
->withExceptions(function (Exceptions $exceptions): void { ->withExceptions(function (Exceptions $exceptions): void {
// $exceptions->report(function (Throwable $exception): void {
if ($exception instanceof ThrottleRequestsException
|| $exception instanceof ValidationException
|| $exception instanceof HttpResponseException) {
return;
}
if ($exception instanceof HttpExceptionInterface
&& in_array($exception->getStatusCode(), [401, 403, 404, 419, 422, 429], true)) {
return;
}
try {
$codigo = (string) $exception->getCode();
if ($codigo === '') {
$codigo = class_basename($exception);
}
$url = app()->runningInConsole()
? 'console'
: (string) request()?->fullUrl();
Error::query()->create([
'codigo' => mb_substr($codigo, 0, 255),
'mensaje' => mb_substr((string) ($exception->getMessage() ?: class_basename($exception)), 0, 65000),
'track_trace' => mb_substr($exception->getTraceAsString(), 0, 65000),
'url' => mb_substr($url !== '' ? $url : 'desconocida', 0, 255),
'fecha_hora' => now(),
]);
} catch (Throwable $loggingException) {
// Evita que un error al registrar el error rompa el flujo original.
}
});
$exceptions->render(function (ThrottleRequestsException $exception, Request $request) {
$retryAfter = (int) ($exception->getHeaders()['Retry-After'] ?? 60);
$retryAfter = max($retryAfter, 1);
$minutos = intdiv($retryAfter, 60);
$segundos = $retryAfter % 60;
$partesTiempo = [];
if ($minutos > 0) {
$partesTiempo[] = $minutos . ' minuto' . ($minutos === 1 ? '' : 's');
}
if ($segundos > 0 || empty($partesTiempo)) {
$partesTiempo[] = $segundos . ' segundo' . ($segundos === 1 ? '' : 's');
}
$mensaje = 'Demasiados intentos. Esperá ' . implode(' y ', $partesTiempo) . ' antes de volver a intentar.';
if ($request->expectsJson()) {
return response()->json([
'message' => $mensaje,
'retry_after' => $retryAfter,
], 429, $exception->getHeaders());
}
$sessionKey = $request->is('login/*') ? 'login_error' : 'recuperar_error';
return back()
->withInput($request->except([
'contra',
'contra_confirmation',
'contra_actual',
'contra_actual_secreta',
]))
->with($sessionKey, $mensaje);
});
$exceptions->render(function (TokenMismatchException $exception, Request $request) {
$mensaje = 'La sesión cambió en otra pestaña o expiró. Recargá la página y volvé a intentar.';
if ($request->expectsJson()) {
return response()->json([
'message' => $mensaje,
], 419);
}
return back()
->withInput($request->except([
'contra',
'contra_confirmation',
'contra_actual',
'contra_actual_secreta',
]))
->withErrors(['csrf' => $mensaje]);
});
})->create(); })->create();
+3 -1
View File
@@ -7,8 +7,10 @@
"license": "MIT", "license": "MIT",
"require": { "require": {
"php": "^8.2", "php": "^8.2",
"barryvdh/laravel-dompdf": "^3.1",
"laravel/framework": "^12.0", "laravel/framework": "^12.0",
"laravel/tinker": "^2.10.1" "laravel/tinker": "^2.10.1",
"spatie/laravel-backup": "^9.3"
}, },
"require-dev": { "require-dev": {
"fakerphp/faker": "^1.23", "fakerphp/faker": "^1.23",
Generated
+883 -1
View File
@@ -4,8 +4,85 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "c514d8f7b9fc5970bdd94287905ef584", "content-hash": "6a842c6b26c34979722aa85fd18609e1",
"packages": [ "packages": [
{
"name": "barryvdh/laravel-dompdf",
"version": "v3.1.2",
"source": {
"type": "git",
"url": "https://github.com/barryvdh/laravel-dompdf.git",
"reference": "ee3b72b19ccdf57d0243116ecb2b90261344dedc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/ee3b72b19ccdf57d0243116ecb2b90261344dedc",
"reference": "ee3b72b19ccdf57d0243116ecb2b90261344dedc",
"shasum": ""
},
"require": {
"dompdf/dompdf": "^3.0",
"illuminate/support": "^9|^10|^11|^12|^13.0",
"php": "^8.1"
},
"require-dev": {
"larastan/larastan": "^2.7|^3.0",
"orchestra/testbench": "^7|^8|^9.16|^10|^11.0",
"phpro/grumphp": "^2.5",
"squizlabs/php_codesniffer": "^3.5"
},
"type": "library",
"extra": {
"laravel": {
"aliases": {
"PDF": "Barryvdh\\DomPDF\\Facade\\Pdf",
"Pdf": "Barryvdh\\DomPDF\\Facade\\Pdf"
},
"providers": [
"Barryvdh\\DomPDF\\ServiceProvider"
]
},
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"Barryvdh\\DomPDF\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Barry vd. Heuvel",
"email": "barryvdh@gmail.com"
}
],
"description": "A DOMPDF Wrapper for Laravel",
"keywords": [
"dompdf",
"laravel",
"pdf"
],
"support": {
"issues": "https://github.com/barryvdh/laravel-dompdf/issues",
"source": "https://github.com/barryvdh/laravel-dompdf/tree/v3.1.2"
},
"funding": [
{
"url": "https://fruitcake.nl",
"type": "custom"
},
{
"url": "https://github.com/barryvdh",
"type": "github"
}
],
"time": "2026-02-21T08:51:10+00:00"
},
{ {
"name": "brick/math", "name": "brick/math",
"version": "0.14.8", "version": "0.14.8",
@@ -377,6 +454,161 @@
], ],
"time": "2024-02-05T11:56:58+00:00" "time": "2024-02-05T11:56:58+00:00"
}, },
{
"name": "dompdf/dompdf",
"version": "v3.1.5",
"source": {
"type": "git",
"url": "https://github.com/dompdf/dompdf.git",
"reference": "f11ead23a8a76d0ff9bbc6c7c8fd7e05ca328496"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dompdf/dompdf/zipball/f11ead23a8a76d0ff9bbc6c7c8fd7e05ca328496",
"reference": "f11ead23a8a76d0ff9bbc6c7c8fd7e05ca328496",
"shasum": ""
},
"require": {
"dompdf/php-font-lib": "^1.0.0",
"dompdf/php-svg-lib": "^1.0.0",
"ext-dom": "*",
"ext-mbstring": "*",
"masterminds/html5": "^2.0",
"php": "^7.1 || ^8.0"
},
"require-dev": {
"ext-gd": "*",
"ext-json": "*",
"ext-zip": "*",
"mockery/mockery": "^1.3",
"phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11",
"squizlabs/php_codesniffer": "^3.5",
"symfony/process": "^4.4 || ^5.4 || ^6.2 || ^7.0"
},
"suggest": {
"ext-gd": "Needed to process images",
"ext-gmagick": "Improves image processing performance",
"ext-imagick": "Improves image processing performance",
"ext-zlib": "Needed for pdf stream compression"
},
"type": "library",
"autoload": {
"psr-4": {
"Dompdf\\": "src/"
},
"classmap": [
"lib/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1"
],
"authors": [
{
"name": "The Dompdf Community",
"homepage": "https://github.com/dompdf/dompdf/blob/master/AUTHORS.md"
}
],
"description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
"homepage": "https://github.com/dompdf/dompdf",
"support": {
"issues": "https://github.com/dompdf/dompdf/issues",
"source": "https://github.com/dompdf/dompdf/tree/v3.1.5"
},
"time": "2026-03-03T13:54:37+00:00"
},
{
"name": "dompdf/php-font-lib",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/dompdf/php-font-lib.git",
"reference": "a6e9a688a2a80016ac080b97be73d3e10c444c9a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/a6e9a688a2a80016ac080b97be73d3e10c444c9a",
"reference": "a6e9a688a2a80016ac080b97be73d3e10c444c9a",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": "^7.1 || ^8.0"
},
"require-dev": {
"phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11 || ^12"
},
"type": "library",
"autoload": {
"psr-4": {
"FontLib\\": "src/FontLib"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1-or-later"
],
"authors": [
{
"name": "The FontLib Community",
"homepage": "https://github.com/dompdf/php-font-lib/blob/master/AUTHORS.md"
}
],
"description": "A library to read, parse, export and make subsets of different types of font files.",
"homepage": "https://github.com/dompdf/php-font-lib",
"support": {
"issues": "https://github.com/dompdf/php-font-lib/issues",
"source": "https://github.com/dompdf/php-font-lib/tree/1.0.2"
},
"time": "2026-01-20T14:10:26+00:00"
},
{
"name": "dompdf/php-svg-lib",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/dompdf/php-svg-lib.git",
"reference": "8259ffb930817e72b1ff1caef5d226501f3dfeb1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/8259ffb930817e72b1ff1caef5d226501f3dfeb1",
"reference": "8259ffb930817e72b1ff1caef5d226501f3dfeb1",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": "^7.1 || ^8.0",
"sabberworm/php-css-parser": "^8.4 || ^9.0"
},
"require-dev": {
"phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11"
},
"type": "library",
"autoload": {
"psr-4": {
"Svg\\": "src/Svg"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0-or-later"
],
"authors": [
{
"name": "The SvgLib Community",
"homepage": "https://github.com/dompdf/php-svg-lib/blob/master/AUTHORS.md"
}
],
"description": "A library to read, parse and export to PDF SVG files.",
"homepage": "https://github.com/dompdf/php-svg-lib",
"support": {
"issues": "https://github.com/dompdf/php-svg-lib/issues",
"source": "https://github.com/dompdf/php-svg-lib/tree/1.0.2"
},
"time": "2026-01-02T16:01:13+00:00"
},
{ {
"name": "dragonmantank/cron-expression", "name": "dragonmantank/cron-expression",
"version": "v3.6.0", "version": "v3.6.0",
@@ -2019,6 +2251,73 @@
], ],
"time": "2026-01-15T06:54:53+00:00" "time": "2026-01-15T06:54:53+00:00"
}, },
{
"name": "masterminds/html5",
"version": "2.10.0",
"source": {
"type": "git",
"url": "https://github.com/Masterminds/html5-php.git",
"reference": "fcf91eb64359852f00d921887b219479b4f21251"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fcf91eb64359852f00d921887b219479b4f21251",
"reference": "fcf91eb64359852f00d921887b219479b4f21251",
"shasum": ""
},
"require": {
"ext-dom": "*",
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.7-dev"
}
},
"autoload": {
"psr-4": {
"Masterminds\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Matt Butcher",
"email": "technosophos@gmail.com"
},
{
"name": "Matt Farina",
"email": "matt@mattfarina.com"
},
{
"name": "Asmir Mustafic",
"email": "goetas@gmail.com"
}
],
"description": "An HTML5 parser and serializer.",
"homepage": "http://masterminds.github.io/html5-php",
"keywords": [
"HTML5",
"dom",
"html",
"parser",
"querypath",
"serializer",
"xml"
],
"support": {
"issues": "https://github.com/Masterminds/html5-php/issues",
"source": "https://github.com/Masterminds/html5-php/tree/2.10.0"
},
"time": "2025-07-25T09:04:22+00:00"
},
{ {
"name": "monolog/monolog", "name": "monolog/monolog",
"version": "3.10.0", "version": "3.10.0",
@@ -3294,6 +3593,446 @@
}, },
"time": "2025-12-14T04:43:48+00:00" "time": "2025-12-14T04:43:48+00:00"
}, },
{
"name": "sabberworm/php-css-parser",
"version": "v9.3.0",
"source": {
"type": "git",
"url": "https://github.com/MyIntervals/PHP-CSS-Parser.git",
"reference": "88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949",
"reference": "88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949",
"shasum": ""
},
"require": {
"ext-iconv": "*",
"php": "^7.2.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
"thecodingmachine/safe": "^1.3 || ^2.5 || ^3.4"
},
"require-dev": {
"php-parallel-lint/php-parallel-lint": "1.4.0",
"phpstan/extension-installer": "1.4.3",
"phpstan/phpstan": "1.12.32 || 2.1.32",
"phpstan/phpstan-phpunit": "1.4.2 || 2.0.8",
"phpstan/phpstan-strict-rules": "1.6.2 || 2.0.7",
"phpunit/phpunit": "8.5.52",
"rawr/phpunit-data-provider": "3.3.1",
"rector/rector": "1.2.10 || 2.2.8",
"rector/type-perfect": "1.0.0 || 2.1.0",
"squizlabs/php_codesniffer": "4.0.1",
"thecodingmachine/phpstan-safe-rule": "1.2.0 || 1.4.1"
},
"suggest": {
"ext-mbstring": "for parsing UTF-8 CSS"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "9.4.x-dev"
}
},
"autoload": {
"files": [
"src/Rule/Rule.php",
"src/RuleSet/RuleContainer.php"
],
"psr-4": {
"Sabberworm\\CSS\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Raphael Schweikert"
},
{
"name": "Oliver Klee",
"email": "github@oliverklee.de"
},
{
"name": "Jake Hotson",
"email": "jake.github@qzdesign.co.uk"
}
],
"description": "Parser for CSS Files written in PHP",
"homepage": "https://www.sabberworm.com/blog/2010/6/10/php-css-parser",
"keywords": [
"css",
"parser",
"stylesheet"
],
"support": {
"issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues",
"source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v9.3.0"
},
"time": "2026-03-03T17:31:43+00:00"
},
{
"name": "spatie/db-dumper",
"version": "3.8.3",
"source": {
"type": "git",
"url": "https://github.com/spatie/db-dumper.git",
"reference": "eac3221fbe27fac51f388600d27b67b1b079406e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/db-dumper/zipball/eac3221fbe27fac51f388600d27b67b1b079406e",
"reference": "eac3221fbe27fac51f388600d27b67b1b079406e",
"shasum": ""
},
"require": {
"php": "^8.0",
"symfony/process": "^5.0|^6.0|^7.0|^8.0"
},
"require-dev": {
"pestphp/pest": "^1.22"
},
"type": "library",
"autoload": {
"psr-4": {
"Spatie\\DbDumper\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Freek Van der Herten",
"email": "freek@spatie.be",
"homepage": "https://spatie.be",
"role": "Developer"
}
],
"description": "Dump databases",
"homepage": "https://github.com/spatie/db-dumper",
"keywords": [
"database",
"db-dumper",
"dump",
"mysqldump",
"spatie"
],
"support": {
"source": "https://github.com/spatie/db-dumper/tree/3.8.3"
},
"funding": [
{
"url": "https://spatie.be/open-source/support-us",
"type": "custom"
},
{
"url": "https://github.com/spatie",
"type": "github"
}
],
"time": "2026-01-05T16:26:03+00:00"
},
{
"name": "spatie/laravel-backup",
"version": "9.3.6",
"source": {
"type": "git",
"url": "https://github.com/spatie/laravel-backup.git",
"reference": "d378a07b580aa8bf440b50decdbab7b5d6f63c46"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/laravel-backup/zipball/d378a07b580aa8bf440b50decdbab7b5d6f63c46",
"reference": "d378a07b580aa8bf440b50decdbab7b5d6f63c46",
"shasum": ""
},
"require": {
"ext-zip": "^1.14.0",
"illuminate/console": "^10.10.0|^11.0|^12.0",
"illuminate/contracts": "^10.10.0|^11.0|^12.0",
"illuminate/events": "^10.10.0|^11.0|^12.0",
"illuminate/filesystem": "^10.10.0|^11.0|^12.0",
"illuminate/notifications": "^10.10.0|^11.0|^12.0",
"illuminate/support": "^10.10.0|^11.0|^12.0",
"league/flysystem": "^3.0",
"php": "^8.2",
"spatie/db-dumper": "^3.8",
"spatie/laravel-package-tools": "^1.6.2",
"spatie/laravel-signal-aware-command": "^1.2|^2.0",
"spatie/temporary-directory": "^2.0",
"symfony/console": "^6.0|^7.0",
"symfony/finder": "^6.0|^7.0"
},
"require-dev": {
"composer-runtime-api": "^2.0",
"ext-pcntl": "*",
"larastan/larastan": "^2.7.0|^3.0",
"laravel/slack-notification-channel": "^2.5|^3.0",
"league/flysystem-aws-s3-v3": "^2.0|^3.0",
"mockery/mockery": "^1.4",
"orchestra/testbench": "^8.0|^9.0|^10.0",
"pestphp/pest": "^1.20|^2.0|^3.0|^4.0",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan-deprecation-rules": "^1.0",
"phpstan/phpstan-phpunit": "^1.1",
"rector/rector": "^1.1"
},
"suggest": {
"laravel/slack-notification-channel": "Required for sending notifications via Slack"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Spatie\\Backup\\BackupServiceProvider"
]
}
},
"autoload": {
"files": [
"src/Helpers/functions.php"
],
"psr-4": {
"Spatie\\Backup\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Freek Van der Herten",
"email": "freek@spatie.be",
"homepage": "https://spatie.be",
"role": "Developer"
}
],
"description": "A Laravel package to backup your application",
"homepage": "https://github.com/spatie/laravel-backup",
"keywords": [
"backup",
"database",
"laravel-backup",
"spatie"
],
"support": {
"issues": "https://github.com/spatie/laravel-backup/issues",
"source": "https://github.com/spatie/laravel-backup/tree/9.3.6"
},
"funding": [
{
"url": "https://github.com/sponsors/spatie",
"type": "github"
},
{
"url": "https://spatie.be/open-source/support-us",
"type": "other"
}
],
"time": "2025-11-05T11:25:01+00:00"
},
{
"name": "spatie/laravel-package-tools",
"version": "1.93.0",
"source": {
"type": "git",
"url": "https://github.com/spatie/laravel-package-tools.git",
"reference": "0d097bce95b2bf6802fb1d83e1e753b0f5a948e7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/0d097bce95b2bf6802fb1d83e1e753b0f5a948e7",
"reference": "0d097bce95b2bf6802fb1d83e1e753b0f5a948e7",
"shasum": ""
},
"require": {
"illuminate/contracts": "^10.0|^11.0|^12.0|^13.0",
"php": "^8.1"
},
"require-dev": {
"mockery/mockery": "^1.5",
"orchestra/testbench": "^8.0|^9.2|^10.0|^11.0",
"pestphp/pest": "^2.1|^3.1|^4.0",
"phpunit/php-code-coverage": "^10.0|^11.0|^12.0",
"phpunit/phpunit": "^10.5|^11.5|^12.5",
"spatie/pest-plugin-test-time": "^2.2|^3.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Spatie\\LaravelPackageTools\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Freek Van der Herten",
"email": "freek@spatie.be",
"role": "Developer"
}
],
"description": "Tools for creating Laravel packages",
"homepage": "https://github.com/spatie/laravel-package-tools",
"keywords": [
"laravel-package-tools",
"spatie"
],
"support": {
"issues": "https://github.com/spatie/laravel-package-tools/issues",
"source": "https://github.com/spatie/laravel-package-tools/tree/1.93.0"
},
"funding": [
{
"url": "https://github.com/spatie",
"type": "github"
}
],
"time": "2026-02-21T12:49:54+00:00"
},
{
"name": "spatie/laravel-signal-aware-command",
"version": "2.1.2",
"source": {
"type": "git",
"url": "https://github.com/spatie/laravel-signal-aware-command.git",
"reference": "54dcc1efd152bfb3eb0faf56a5fc28569b864b5d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/laravel-signal-aware-command/zipball/54dcc1efd152bfb3eb0faf56a5fc28569b864b5d",
"reference": "54dcc1efd152bfb3eb0faf56a5fc28569b864b5d",
"shasum": ""
},
"require": {
"illuminate/contracts": "^11.0|^12.0|^13.0",
"php": "^8.2",
"spatie/laravel-package-tools": "^1.4.3",
"symfony/console": "^7.0|^8.0"
},
"require-dev": {
"brianium/paratest": "^6.2|^7.0",
"ext-pcntl": "*",
"nunomaduro/collision": "^5.3|^6.0|^7.0|^8.0",
"orchestra/testbench": "^9.0|^10.0",
"pestphp/pest-plugin-laravel": "^1.3|^2.0|^3.0",
"phpunit/phpunit": "^9.5|^10|^11",
"spatie/laravel-ray": "^1.17"
},
"type": "library",
"extra": {
"laravel": {
"aliases": {
"Signal": "Spatie\\SignalAwareCommand\\Facades\\Signal"
},
"providers": [
"Spatie\\SignalAwareCommand\\SignalAwareCommandServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Spatie\\SignalAwareCommand\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Freek Van der Herten",
"email": "freek@spatie.be",
"role": "Developer"
}
],
"description": "Handle signals in artisan commands",
"homepage": "https://github.com/spatie/laravel-signal-aware-command",
"keywords": [
"laravel",
"laravel-signal-aware-command",
"spatie"
],
"support": {
"issues": "https://github.com/spatie/laravel-signal-aware-command/issues",
"source": "https://github.com/spatie/laravel-signal-aware-command/tree/2.1.2"
},
"funding": [
{
"url": "https://github.com/spatie",
"type": "github"
}
],
"time": "2026-02-22T08:16:31+00:00"
},
{
"name": "spatie/temporary-directory",
"version": "2.3.1",
"source": {
"type": "git",
"url": "https://github.com/spatie/temporary-directory.git",
"reference": "662e481d6ec07ef29fd05010433428851a42cd07"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/temporary-directory/zipball/662e481d6ec07ef29fd05010433428851a42cd07",
"reference": "662e481d6ec07ef29fd05010433428851a42cd07",
"shasum": ""
},
"require": {
"php": "^8.0"
},
"require-dev": {
"phpunit/phpunit": "^9.5"
},
"type": "library",
"autoload": {
"psr-4": {
"Spatie\\TemporaryDirectory\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Alex Vanderbist",
"email": "alex@spatie.be",
"homepage": "https://spatie.be",
"role": "Developer"
}
],
"description": "Easily create, use and destroy temporary directories",
"homepage": "https://github.com/spatie/temporary-directory",
"keywords": [
"php",
"spatie",
"temporary-directory"
],
"support": {
"issues": "https://github.com/spatie/temporary-directory/issues",
"source": "https://github.com/spatie/temporary-directory/tree/2.3.1"
},
"funding": [
{
"url": "https://spatie.be/open-source/support-us",
"type": "custom"
},
{
"url": "https://github.com/spatie",
"type": "github"
}
],
"time": "2026-01-12T07:42:22+00:00"
},
{ {
"name": "symfony/clock", "name": "symfony/clock",
"version": "v7.4.0", "version": "v7.4.0",
@@ -5795,6 +6534,149 @@
], ],
"time": "2026-02-15T10:53:20+00:00" "time": "2026-02-15T10:53:20+00:00"
}, },
{
"name": "thecodingmachine/safe",
"version": "v3.4.0",
"source": {
"type": "git",
"url": "https://github.com/thecodingmachine/safe.git",
"reference": "705683a25bacf0d4860c7dea4d7947bfd09eea19"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thecodingmachine/safe/zipball/705683a25bacf0d4860c7dea4d7947bfd09eea19",
"reference": "705683a25bacf0d4860c7dea4d7947bfd09eea19",
"shasum": ""
},
"require": {
"php": "^8.1"
},
"require-dev": {
"php-parallel-lint/php-parallel-lint": "^1.4",
"phpstan/phpstan": "^2",
"phpunit/phpunit": "^10",
"squizlabs/php_codesniffer": "^3.2"
},
"type": "library",
"autoload": {
"files": [
"lib/special_cases.php",
"generated/apache.php",
"generated/apcu.php",
"generated/array.php",
"generated/bzip2.php",
"generated/calendar.php",
"generated/classobj.php",
"generated/com.php",
"generated/cubrid.php",
"generated/curl.php",
"generated/datetime.php",
"generated/dir.php",
"generated/eio.php",
"generated/errorfunc.php",
"generated/exec.php",
"generated/fileinfo.php",
"generated/filesystem.php",
"generated/filter.php",
"generated/fpm.php",
"generated/ftp.php",
"generated/funchand.php",
"generated/gettext.php",
"generated/gmp.php",
"generated/gnupg.php",
"generated/hash.php",
"generated/ibase.php",
"generated/ibmDb2.php",
"generated/iconv.php",
"generated/image.php",
"generated/imap.php",
"generated/info.php",
"generated/inotify.php",
"generated/json.php",
"generated/ldap.php",
"generated/libxml.php",
"generated/lzf.php",
"generated/mailparse.php",
"generated/mbstring.php",
"generated/misc.php",
"generated/mysql.php",
"generated/mysqli.php",
"generated/network.php",
"generated/oci8.php",
"generated/opcache.php",
"generated/openssl.php",
"generated/outcontrol.php",
"generated/pcntl.php",
"generated/pcre.php",
"generated/pgsql.php",
"generated/posix.php",
"generated/ps.php",
"generated/pspell.php",
"generated/readline.php",
"generated/rnp.php",
"generated/rpminfo.php",
"generated/rrd.php",
"generated/sem.php",
"generated/session.php",
"generated/shmop.php",
"generated/sockets.php",
"generated/sodium.php",
"generated/solr.php",
"generated/spl.php",
"generated/sqlsrv.php",
"generated/ssdeep.php",
"generated/ssh2.php",
"generated/stream.php",
"generated/strings.php",
"generated/swoole.php",
"generated/uodbc.php",
"generated/uopz.php",
"generated/url.php",
"generated/var.php",
"generated/xdiff.php",
"generated/xml.php",
"generated/xmlrpc.php",
"generated/yaml.php",
"generated/yaz.php",
"generated/zip.php",
"generated/zlib.php"
],
"classmap": [
"lib/DateTime.php",
"lib/DateTimeImmutable.php",
"lib/Exceptions/",
"generated/Exceptions/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "PHP core functions that throw exceptions instead of returning FALSE on error",
"support": {
"issues": "https://github.com/thecodingmachine/safe/issues",
"source": "https://github.com/thecodingmachine/safe/tree/v3.4.0"
},
"funding": [
{
"url": "https://github.com/OskarStark",
"type": "github"
},
{
"url": "https://github.com/shish",
"type": "github"
},
{
"url": "https://github.com/silasjoisten",
"type": "github"
},
{
"url": "https://github.com/staabm",
"type": "github"
}
],
"time": "2026-02-04T18:08:13+00:00"
},
{ {
"name": "tijsverkoyen/css-to-inline-styles", "name": "tijsverkoyen/css-to-inline-styles",
"version": "v2.4.0", "version": "v2.4.0",
+4 -4
View File
@@ -65,7 +65,7 @@ return [
| |
*/ */
'timezone' => 'UTC', 'timezone' => 'America/Argentina/Buenos_Aires',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@@ -78,11 +78,11 @@ return [
| |
*/ */
'locale' => env('APP_LOCALE', 'en'), 'locale' => env('APP_LOCALE', 'es'),
'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'), 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'es'),
'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'), 'faker_locale' => env('APP_FAKER_LOCALE', 'es_AR'),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
+344
View File
@@ -0,0 +1,344 @@
<?php
return [
'backup' => [
/*
* The name of this application. You can use this name to monitor
* the backups.
*/
'name' => env('APP_NAME', 'AbogadasDelLitoral'),
'source' => [
'files' => [
/*
* The list of directories and files that will be included in the backup.
*/
'include' => array_values(array_filter([
storage_path('app/public/fotos'),
storage_path('app/private/documentacion-clientes'),
public_path('images/profesionales'),
public_path('images/servicios'),
public_path('images/bugs'),
public_path('uploads/documentacion-clientes'),
filter_var(env('BACKUP_INCLUDE_ENV', false), FILTER_VALIDATE_BOOLEAN) ? base_path('.env') : null,
], static fn (mixed $path): bool => is_string($path) && (is_dir($path) || is_file($path)))),
/*
* These directories and files will be excluded from the backup.
*
* Directories used by the backup process will automatically be excluded.
*/
'exclude' => [],
/*
* Determines if symlinks should be followed.
*/
'follow_links' => false,
/*
* Determines if it should avoid unreadable folders.
*/
'ignore_unreadable_directories' => false,
/*
* This path is used to make directories in resulting zip-file relative
* Set to `null` to include complete absolute path
* Example: base_path()
*/
'relative_path' => base_path(),
],
/*
* The names of the connections to the databases that should be backed up
* MySQL, PostgreSQL, SQLite and Mongo databases are supported.
*
* The content of the database dump may be customized for each connection
* by adding a 'dump' key to the connection settings in config/database.php.
* E.g.
* 'mysql' => [
* ...
* 'dump' => [
* 'exclude_tables' => [
* 'table_to_exclude_from_backup',
* 'another_table_to_exclude'
* ]
* ],
* ],
*
* If you are using only InnoDB tables on a MySQL server, you can
* also supply the useSingleTransaction option to avoid table locking.
*
* E.g.
* 'mysql' => [
* ...
* 'dump' => [
* 'useSingleTransaction' => true,
* ],
* ],
*
* For a complete list of available customization options, see https://github.com/spatie/db-dumper
*/
'databases' => [
env('DB_CONNECTION', 'mysql'),
],
],
/*
* The database dump can be compressed to decrease disk space usage.
*
* Out of the box Laravel-backup supplies
* Spatie\DbDumper\Compressors\GzipCompressor::class.
*
* You can also create custom compressor. More info on that here:
* https://github.com/spatie/db-dumper#using-compression
*
* If you do not want any compressor at all, set it to null.
*/
'database_dump_compressor' => null,
/*
* If specified, the database dumped file name will contain a timestamp (e.g.: 'Y-m-d-H-i-s').
*/
'database_dump_file_timestamp_format' => null,
/*
* The base of the dump filename, either 'database' or 'connection'
*
* If 'database' (default), the dumped filename will contain the database name.
* If 'connection', the dumped filename will contain the connection name.
*/
'database_dump_filename_base' => 'database',
/*
* The file extension used for the database dump files.
*
* If not specified, the file extension will be .archive for MongoDB and .sql for all other databases
* The file extension should be specified without a leading .
*/
'database_dump_file_extension' => '',
'destination' => [
/*
* The compression algorithm to be used for creating the zip archive.
*
* If backing up only database, you may choose gzip compression for db dump and no compression at zip.
*
* Some common algorithms are listed below:
* ZipArchive::CM_STORE (no compression at all; set 0 as compression level)
* ZipArchive::CM_DEFAULT
* ZipArchive::CM_DEFLATE
* ZipArchive::CM_BZIP2
* ZipArchive::CM_XZ
*
* For more check https://www.php.net/manual/zip.constants.php and confirm it's supported by your system.
*/
'compression_method' => ZipArchive::CM_DEFAULT,
/*
* The compression level corresponding to the used algorithm; an integer between 0 and 9.
*
* Check supported levels for the chosen algorithm, usually 1 means the fastest and weakest compression,
* while 9 the slowest and strongest one.
*
* Setting of 0 for some algorithms may switch to the strongest compression.
*/
'compression_level' => 9,
/*
* The filename prefix used for the backup zip file.
*/
'filename_prefix' => '',
/*
* The disk names on which the backups will be stored.
*/
'disks' => [
...array_values(array_filter(array_map('trim', explode(',', env('BACKUP_DESTINATION_DISK', 'local'))))),
],
],
/*
* The directory where the temporary files will be stored.
*/
'temporary_directory' => storage_path('app/backup-temp'),
/*
* The password to be used for archive encryption.
* Set to `null` to disable encryption.
*/
'password' => env('BACKUP_ARCHIVE_PASSWORD') ?: null,
/*
* The encryption algorithm to be used for archive encryption.
* You can set it to `null` or `false` to disable encryption.
*
* When set to 'default', we'll use ZipArchive::EM_AES_256 if it is
* available on your system.
*/
'encryption' => env('BACKUP_ARCHIVE_PASSWORD') ? 'default' : false,
/*
* The number of attempts, in case the backup command encounters an exception
*/
'tries' => 1,
/*
* The number of seconds to wait before attempting a new backup if the previous try failed
* Set to `0` for none
*/
'retry_delay' => 0,
],
/*
* You can get notified when specific events occur. Out of the box you can use 'mail' and 'slack'.
* For Slack you need to install laravel/slack-notification-channel.
*
* You can also use your own notification classes, just make sure the class is named after one of
* the `Spatie\Backup\Notifications\Notifications` classes.
*/
'notifications' => [
'notifications' => [
\Spatie\Backup\Notifications\Notifications\BackupHasFailedNotification::class => ['mail'],
\Spatie\Backup\Notifications\Notifications\UnhealthyBackupWasFoundNotification::class => ['mail'],
\Spatie\Backup\Notifications\Notifications\CleanupHasFailedNotification::class => ['mail'],
\Spatie\Backup\Notifications\Notifications\BackupWasSuccessfulNotification::class => [],
\Spatie\Backup\Notifications\Notifications\HealthyBackupWasFoundNotification::class => [],
\Spatie\Backup\Notifications\Notifications\CleanupWasSuccessfulNotification::class => [],
],
/*
* Here you can specify the notifiable to which the notifications should be sent. The default
* notifiable will use the variables specified in this config file.
*/
'notifiable' => \Spatie\Backup\Notifications\Notifiable::class,
'mail' => [
'to' => env('BACKUP_MAIL_TO', env('MAIL_FROM_ADDRESS', 'admin@example.com')),
'from' => [
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
'name' => env('MAIL_FROM_NAME', 'Example'),
],
],
'slack' => [
'webhook_url' => '',
/*
* If this is set to null the default channel of the webhook will be used.
*/
'channel' => null,
'username' => null,
'icon' => null,
],
'discord' => [
'webhook_url' => '',
/*
* If this is an empty string, the name field on the webhook will be used.
*/
'username' => '',
/*
* If this is an empty string, the avatar on the webhook will be used.
*/
'avatar_url' => '',
],
],
/*
* Here you can specify which backups should be monitored.
* If a backup does not meet the specified requirements the
* UnHealthyBackupWasFound event will be fired.
*/
'monitor_backups' => [
[
'name' => env('APP_NAME', 'laravel-backup'),
'disks' => array_values(array_filter(array_map('trim', explode(',', env('BACKUP_DESTINATION_DISK', 'local'))))),
'health_checks' => [
\Spatie\Backup\Tasks\Monitor\HealthChecks\MaximumAgeInDays::class => (int) env('BACKUP_MAX_AGE_DAYS', 7),
\Spatie\Backup\Tasks\Monitor\HealthChecks\MaximumStorageInMegabytes::class => (int) env('BACKUP_MAX_STORAGE_MB', 5000),
],
],
/*
[
'name' => 'name of the second app',
'disks' => ['local', 's3'],
'health_checks' => [
\Spatie\Backup\Tasks\Monitor\HealthChecks\MaximumAgeInDays::class => 1,
\Spatie\Backup\Tasks\Monitor\HealthChecks\MaximumStorageInMegabytes::class => 5000,
],
],
*/
],
'cleanup' => [
/*
* The strategy that will be used to cleanup old backups. The default strategy
* will keep all backups for a certain amount of days. After that period only
* a daily backup will be kept. After that period only weekly backups will
* be kept and so on.
*
* No matter how you configure it the default strategy will never
* delete the newest backup.
*/
'strategy' => \Spatie\Backup\Tasks\Cleanup\Strategies\DefaultStrategy::class,
'default_strategy' => [
/*
* The number of days for which backups must be kept.
*/
'keep_all_backups_for_days' => (int) env('BACKUP_KEEP_ALL_DAYS', 7),
/*
* After the "keep_all_backups_for_days" period is over, the most recent backup
* of that day will be kept. Older backups within the same day will be removed.
* If you create backups only once a day, no backups will be removed yet.
*/
'keep_daily_backups_for_days' => (int) env('BACKUP_KEEP_DAILY_DAYS', 16),
/*
* After the "keep_daily_backups_for_days" period is over, the most recent backup
* of that week will be kept. Older backups within the same week will be removed.
* If you create backups only once a week, no backups will be removed yet.
*/
'keep_weekly_backups_for_weeks' => (int) env('BACKUP_KEEP_WEEKLY_WEEKS', 8),
/*
* After the "keep_weekly_backups_for_weeks" period is over, the most recent backup
* of that month will be kept. Older backups within the same month will be removed.
*/
'keep_monthly_backups_for_months' => (int) env('BACKUP_KEEP_MONTHLY_MONTHS', 4),
/*
* After the "keep_monthly_backups_for_months" period is over, the most recent backup
* of that year will be kept. Older backups within the same year will be removed.
*/
'keep_yearly_backups_for_years' => (int) env('BACKUP_KEEP_YEARLY_YEARS', 2),
/*
* After cleaning up the backups remove the oldest backup until
* this amount of megabytes has been reached.
* Set null for unlimited size.
*/
'delete_oldest_backups_when_using_more_megabytes_than' => (int) env('BACKUP_MAX_STORAGE_LIMIT_MB', 5000),
],
/*
* The number of attempts, in case the cleanup command encounters an exception
*/
'tries' => 1,
/*
* The number of seconds to wait before attempting a new cleanup if the previous try failed
* Set to `0` for none
*/
'retry_delay' => 0,
],
];
+8 -1
View File
@@ -16,7 +16,7 @@ return [
| |
*/ */
'default' => env('DB_CONNECTION', 'sqlite'), 'default' => env('DB_CONNECTION', 'mysql'),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@@ -60,7 +60,13 @@ return [
'engine' => null, 'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([ 'options' => extension_loaded('pdo_mysql') ? array_filter([
(PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'), (PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci',
]) : [], ]) : [],
'dump' => [
'dump_binary_path' => env('DB_DUMP_BINARY_PATH', PHP_OS_FAMILY === 'Windows' ? 'C:/xampp/mysql/bin/' : '/usr/bin/'),
'use_single_transaction' => true,
'timeout' => 60 * 5, // 5 minutos de tiempo límite
],
], ],
'mariadb' => [ 'mariadb' => [
@@ -80,6 +86,7 @@ return [
'engine' => null, 'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([ 'options' => extension_loaded('pdo_mysql') ? array_filter([
(PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'), (PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci',
]) : [], ]) : [],
], ],
+31
View File
@@ -0,0 +1,31 @@
<?php
namespace Database\Factories;
use App\Models\AccionLog;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\AccionLog>
*/
class AccionLogFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = AccionLog::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'descripcion' => $this->faker->sentence(),
];
}
}
@@ -0,0 +1,36 @@
<?php
namespace Database\Factories;
use App\Models\Administrador;
use App\Models\CredencialProfesional;
use App\Models\Persona;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Administrador>
*/
class AdministradorFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = Administrador::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'persona_id' => Persona::factory(),
'dni' => (string) $this->faker->unique()->numberBetween(20000000, 45000000),
'correo' => $this->faker->unique()->safeEmail(),
'credencialprofesional_id' => CredencialProfesional::factory(),
];
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
namespace Database\Factories;
use App\Models\Agenda;
use App\Models\Profesional;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Agenda>
*/
class AgendaFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = Agenda::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'estado' => $this->faker->randomElement(['Activa', 'Inactiva']),
'duracionturno' => $this->faker->randomElement([15, 30, 45, 60]),
'profesional_id' => Profesional::factory(),
];
}
}
+31
View File
@@ -0,0 +1,31 @@
<?php
namespace Database\Factories;
use App\Models\Baja;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Baja>
*/
class BajaFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = Baja::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'motivo' => $this->faker->optional()->sentence(),
];
}
}
+37
View File
@@ -0,0 +1,37 @@
<?php
namespace Database\Factories;
use App\Models\Bug;
use App\Models\FotoBug;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Bug>
*/
class BugFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = Bug::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'titulo' => $this->faker->sentence(4),
'descripcion' => $this->faker->paragraph(),
'prioridad' => $this->faker->randomElement(['baja', 'media', 'alta']),
'estado' => $this->faker->randomElement(['abierto', 'en progreso', 'cerrado']),
'version' => $this->faker->regexify('[0-9]{1,2}\.[0-9]{1,2}\.[0-9]{1,2}'),
'fotobug_id' => $this->faker->optional()->then(fn () => FotoBug::factory()),
];
}
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace Database\Factories;
use App\Models\Cliente;
use App\Models\CredencialCliente;
use App\Models\Persona;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Cliente>
*/
class ClienteFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = Cliente::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'dni' => (string) $this->faker->unique()->numberBetween(20000000, 45000000),
'correo' => $this->faker->unique()->safeEmail(),
'persona_id' => Persona::factory(),
'baja_id' => 1,
'credencialcliente_id' => CredencialCliente::factory(),
];
}
public function configure(): static
{
return $this->afterCreating(function (Cliente $cliente) {
CredencialCliente::where('id', $cliente->credencialcliente_id)
->update([
'correo' => $cliente->correo,
'contra' => Hash::make($cliente->dni),
]);
});
}
}
@@ -0,0 +1,31 @@
<?php
namespace Database\Factories;
use App\Models\ContenidoWeb;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\ContenidoWeb>
*/
class ContenidoWebFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = ContenidoWeb::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'quienessomos' => $this->faker->paragraph(3),
];
}
}
@@ -0,0 +1,35 @@
<?php
namespace Database\Factories;
use App\Models\CredencialCliente;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\CredencialCliente>
*/
class CredencialClienteFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = CredencialCliente::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'contra' => Hash::make('password'),
'correo' => $this->faker->unique()->safeEmail(),
'token' => null,
'fecha_hora' => null,
];
}
}
@@ -0,0 +1,36 @@
<?php
namespace Database\Factories;
use App\Models\CredencialProfesional;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\CredencialProfesional>
*/
class CredencialProfesionalFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = CredencialProfesional::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'usuario' => $this->faker->unique()->userName(),
'contra' => Hash::make('password'),
'rol' => 'Profesional',
'token' => null,
'fecha_hora' => null,
];
}
}
@@ -0,0 +1,35 @@
<?php
namespace Database\Factories;
use App\Models\Agenda;
use App\Models\Dia;
use App\Models\DiaDeAtencion;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\DiaDeAtencion>
*/
class DiaDeAtencionFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = DiaDeAtencion::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'descripcion' => $this->faker->sentence(3),
'agenda_id' => Agenda::factory(),
'dia_id' => Dia::factory(),
];
}
}
+31
View File
@@ -0,0 +1,31 @@
<?php
namespace Database\Factories;
use App\Models\Dia;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Dia>
*/
class DiaFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = Dia::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'descripcion' => $this->faker->randomElement(['Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo']),
];
}
}
@@ -0,0 +1,33 @@
<?php
namespace Database\Factories;
use App\Models\DiaPreferencia;
use App\Models\Formulario;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\DiaPreferencia>
*/
class DiaPreferenciaFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = DiaPreferencia::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'descripcion' => $this->faker->sentence(3),
'formulario_id' => Formulario::factory(),
];
}
}
@@ -0,0 +1,41 @@
<?php
namespace Database\Factories;
use App\Models\Cliente;
use App\Models\DocumentacionCliente;
use App\Models\Profesional;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\DocumentacionCliente>
*/
class DocumentacionClienteFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = DocumentacionCliente::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
$extension = $this->faker->randomElement(['pdf', 'jpg', 'png']);
$nombre = $this->faker->word();
return [
'nombre' => $nombre,
'mime_type' => $this->faker->mimeType(),
'tamanio_bytes' => $this->faker->numberBetween(1024, 10_485_760),
'extension' => $extension,
'cliente_id' => Cliente::factory(),
'profesional_id' => Profesional::factory(),
];
}
}
@@ -0,0 +1,31 @@
<?php
namespace Database\Factories;
use App\Models\EstadoProfesional;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\EstadoProfesional>
*/
class EstadoProfesionalFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = EstadoProfesional::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'descripcion' => $this->faker->randomElement(['Activo', 'Baja']),
];
}
}

Some files were not shown because too many files have changed in this diff Show More