Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Arthurr23/XHealtXperience/llms.txt

Use this file to discover all available pages before exploring further.

Patient management is the foundational module of XHealtXperience. Every appointment, service assignment, and clinical record in the system is anchored to a patient record created inside a specific clinic tenant. The module covers everything from first-time registration and automated code generation, through document uploads and digital consent workflows, all the way to the clinical record history that accompanies each patient throughout their care journey.

Patient Registration Flow

Creating a new patient navigates to a dedicated form at GET /pacientes/create. The form is submitted to POST /pacientes and validated server-side before the record is persisted.

Required vs Optional Fields

nombres
string
required
Patient’s given names. Maximum 255 characters.
apellido_paterno
string
required
Paternal surname. Maximum 255 characters.
fecha_nacimiento
date
required
Date of birth (YYYY-MM-DD). Used to calculate the auto-appended edad attribute.
genero
string
required
One of Masculino, Femenino, or Otro.
status
string
required
Desired initial status. Automatically forced to Pendiente if domicilio or telefono is missing — see Auto-status Logic below.
apellido_materno
string
Maternal surname. Optional; included in the computed nombre_completo attribute.
domicilio
string
Full home address. If omitted, status is overridden to Pendiente automatically.
telefono
string
Mobile phone number (max 20 characters). If omitted, status is overridden to Pendiente automatically.
email
string
Email address for notifications (nullable).
estado_civil
string
Civil status (nullable, max 50 characters).
lugar_nacimiento
string
City or state of birth (nullable).
ocupacion
string
Patient’s occupation (nullable, max 100 characters).
observaciones
string
Free-text clinical observations (nullable).
acepta_notificaciones
boolean
Whether the patient has authorized appointment reminders. Defaults to true in the appointment creation flow.
canal_notificacion_preferido
string
Preferred notification channel (whatsapp, sms, email, or ninguno).

Auto-status Logic

When a patient record is created or updated, the system checks whether both domicilio and telefono are present. If either field is blank, the status is always forced to Pendiente, regardless of what was submitted in the form. This rule is enforced in both store() and update() inside PacienteController:
// PacienteController::store() / update()
if (empty($validated['domicilio']) || empty($validated['telefono'])) {
    $validated['status'] = 'Pendiente';
}
A patient can only reach Activo status once both required contact fields are filled in.

Auto-generated Patient Code

Every new patient automatically receives a unique codigo_paciente during the creating Eloquent event. The format is:
{CLINIC_CODE}PAC{00001}
PartDescriptionExample
CLINIC_CODEFirst 3 uppercase letters of tenant.codigo_clinica, padded to 3 chars with X if shorterCLI
PACFixed literalPAC
00001Zero-padded 5-digit counter from tenant_settings.contador_pacientes00001
Full example: CLIMIPAC00001 The counter is incremented inside a database transaction with lockForUpdate() to guarantee uniqueness under concurrent writes. If a transaction is rolled back and leaves a gap in the counter, the next creation auto-repairs by scanning for the next truly-free code rather than trusting the stored counter blindly.
If codigo_paciente is supplied explicitly at creation time (e.g., during a manual import), the auto-generation is skipped entirely to avoid overwriting the imported code.

Computed Attributes

The Paciente model appends three computed attributes to every JSON response (such as Inertia page props):
AttributeDescription
nombre_completoConcatenates nombres, apellido_paterno, and apellido_materno.
edadAge in years, calculated from fecha_nacimiento using Carbon. Returns null when the field is empty.
datos_faltantesArray of human-readable labels for missing required data. See below.

datos_faltantes Attribute

This attribute is the core of the receptionist’s to-do checklist. It returns a plain array of strings describing exactly what the patient is still missing:
// Paciente::getDatosFaltantesAttribute()
if (!$this->domicilio)                             $faltantes[] = 'Domicilio';
if (!$this->telefono)                              $faltantes[] = 'Teléfono Celular';
if (!$this->path_identificacion_oficial)           $faltantes[] = 'INE / Pasaporte';
if (!$this->path_aviso_privacidad_firmado)         $faltantes[] = 'Firma de Aviso de Privacidad';
if (!$this->path_aviso_confidencialidad_firmado)   $faltantes[] = 'Firma de Aviso de Confidencialidad';
if (!$this->path_contrato_servicios_firmado)       $faltantes[] = 'Contrato de Prestación de Servicios';
if (!$this->path_consentimiento_informado_firmado) $faltantes[] = 'Consentimiento Informado';
An empty array means the patient record is complete. This value is exposed automatically in every Inertia page that receives a paciente prop.

Document Uploads

Identity documents are uploaded via POST /pacientes/{id}/upload-identificacion. The endpoint accepts a multipart/form-data request:
identificacion
file
required
JPEG, PNG, or PDF file; maximum 4 MB.
lado
string
required
frente saves to path_identificacion_oficial; reverso saves to path_identificacion_reverso.
The file is stored under the tenant’s public disk at identificaciones/ and the public URL is written back to the corresponding model field.

Document Path Fields

FieldDocument
path_identificacion_oficialFront of INE / Passport
path_identificacion_reversoReverse side of INE / Passport
path_aviso_privacidad_firmadoSigned privacy notice
path_aviso_confidencialidad_firmadoSigned confidentiality notice
path_contrato_servicios_firmadoSigned services contract
path_consentimiento_informado_firmadoSigned informed consent
path_acuerdo_datos_sensibles_firmadoSigned sensitive-data agreement
Consent documents are signed directly on a canvas element in the browser and submitted as Base64-encoded PNG data to POST /pacientes/{id}/guardar-firma.
tipo_documento
string
required
Which document to sign. One of: privacidad, confidencialidad, contrato, consentimiento, or sensibles.
firma_base64
string
required
The full data:image/png;base64,... string captured from the signature canvas.
The server strips the data URI prefix, decodes the image, saves it under firmas/ on the public disk, and stores the resulting path in the corresponding path_*_firmado column:
tipo_documento valueColumn updated
privacidadpath_aviso_privacidad_firmado
confidencialidadpath_aviso_confidencialidad_firmado
contratopath_contrato_servicios_firmado
consentimientopath_consentimiento_informado_firmado
sensiblespath_acuerdo_datos_sensibles_firmado

Clinical Record (Expediente Clínico)

Each patient can have multiple clinical records associated with them through the expedientes relationship:
// Paciente model
public function expedientes()
{
    return $this->hasMany(ExpedienteClinico::class)->orderBy('created_at', 'desc');
}
Records are returned newest-first. The patient detail view at GET /pacientes/{id} passes the full patient object (including appended attributes) to the Pacientes/Show React component via Inertia.

Route Reference

All routes below sit inside the /{tenant}/ path prefix and require the auth + two_factor + check_inactivity middleware stack unless stated otherwise.
MethodPathDescriptionRequired Role
GET/pacientesList all patients, ordered by apellido_paternoAny authenticated
GET/pacientes/createShow patient registration formAny authenticated
POST/pacientesCreate a new patient recordAny authenticated
GET/pacientes/{id}Patient detail and clinical record summaryAny authenticated
GET/pacientes/{id}/editShow patient edit formAny authenticated
PUT/PATCH/pacientes/{id}Update patient detailsAny authenticated
DELETE/pacientes/{id}Remove patient from the systemAny authenticated
POST/pacientes/{id}/upload-identificacionUpload front or back of ID documentAny authenticated
POST/pacientes/{id}/guardar-firmaSave a digital consent signatureAny authenticated
Deleting a patient is a hard delete. Appointments, service assignments, and clinical records linked to the patient will cascade or null their foreign keys according to each migration’s onDelete policy.

Build docs developers (and LLMs) love