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.

The appointment module is the operational heart of XHealtXperience’s day-to-day clinic workflow. It supports individual and recurring consultation appointments, real-time conflict detection against both existing appointments and agenda blocks, an overbooking override mechanism with full audit trail, and a QR-based check-in flow that works both through an authenticated staff interface and an unauthenticated tablet kiosk placed in the waiting room.

Appointment Lifecycle

Every appointment (Cita) moves through a defined set of statuses. The diagram below shows the normal path and the two terminal exception states:
programada ──► confirmada ──► checkin ──► en_progreso ──► completada
     │               │           │
     └───────────────┴───────────┴──► cancelada
                                  └──► no_show
Status constantValueDescription
ESTATUS_PROGRAMADAprogramadaAppointment has been booked but not yet confirmed.
ESTATUS_CONFIRMADAconfirmadaConfirmed by the patient or clinic staff.
ESTATUS_CHECKINcheckinPatient has checked in at the front desk.
ESTATUS_EN_PROGRESOen_progresoConsultation or procedure is actively in progress.
ESTATUS_COMPLETADAcompletadaAppointment concluded.
ESTATUS_CANCELADAcanceladaAppointment was cancelled (with optional penalty).
ESTATUS_NO_SHOWno_showPatient did not appear.
ESTATUS_ACTIVOS is a convenience constant that groups programada, confirmada, checkin, and en_progreso — it is used throughout availability checks and dashboard queries to filter only live appointments.

Modalidad and Tipo de Cita

Two orthogonal classifiers describe every appointment: modalidad — how the appointment takes place:
  • presencial: In-person visit at the clinic.
  • virtual: Remote/telemedicine consultation.
tipo_cita — what category of appointment this is:
  • consulta: A standard medical consultation. Created through CitaController.
  • cirugia: A surgical procedure. Created through QuirofanoController and always requires a Sala of type quirofano and a ProcedimientoQuirurgico. See Surgical Suites for details.

Scheduling an Appointment

The appointment creation form is served at GET /citas/create and submitted to POST /citas. The controller middleware restricts creation to users with the roles Recepcion, Doctor, Administrador Clinica, or Administrador Profesionista. You can either select an existing patient by ID (paciente_id) or supply minimal data for a new patient inline (nuevo_paciente.*). When creating a new patient inline, the status is automatically set to Pendiente.

Conflict Detection via DisponibilidadService

Before persisting a single appointment, the system calls DisponibilidadService::resumenConflictos(), which executes three independent overlap queries for the requested time window:
  1. Doctor conflict — active appointments assigned to the same medico_id that overlap hora_inicio/hora_fin.
  2. Agenda block conflictBloqueoAgenda records for the same doctor in the same window.
  3. Room conflict — active appointments in the same sala_id that overlap the window.
// DisponibilidadService::resumenConflictos()
$citasMedico = $this->citasEnConflictoMedico($medicoId, $inicio, $fin, $excluirCitaId);
$bloqueos    = $this->bloqueosEnConflicto($medicoId, $inicio, $fin);
$citasSala   = $this->citasEnConflictoSala($salaId, $inicio, $fin, $excluirCitaId);
If any conflicts are found, the method returns a structured array containing lists of conflicting appointments and blocks; otherwise it returns null (clear to proceed). When creating a single appointment, conflicts cause the form to be returned with a conflicto_horario flash key so the React UI can display a warning modal.
When a staff member confirms overbooking by setting overbooking_confirmado = true and providing a notas_override justification, the appointment is saved anyway and the overbooking flag is set to true on the record, with overbooking_aceptado_por pointing to the user who approved it. Use POST /citas/{cita}/resolver-conflicto to resolve a pending conflict on a recurring series occurrence — set accion to aceptar (keeps the appointment as overbooking) or cancelar (cancels that specific occurrence).

Recurring Appointments

An appointment series is created by setting es_recurrente = true in the creation request. Recurrence is controlled by three fields:
recurrencia_tipo
string
How the series repeats: semanal, quincenal, mensual, or personalizada.
recurrencia_intervalo_dias
integer
Used when recurrencia_tipo is personalizada. The number of days between occurrences.
recurrencia_hasta
date
The end date of the series (YYYY-MM-DD). Used when recurrencia_metodo_fin is fecha_hasta.
RecurrenciaService expands the start date into the full list of occurrence dates before the controller persists each one inside a single database transaction. The first occurrence becomes the “anchor” (cita_padre_id = null); all subsequent occurrences store its ID in cita_padre_id. Recurring series occurrences with schedule conflicts are not blocked — they are created with conflicto_pendiente = true for later manual review, and a count of flagged occurrences is included in the success flash message. To cancel every future occurrence in a series at once, pass alcance = serie when calling CitaController::cancelar() once that route is available.

QR Token and Check-In

A UUID v4 qr_token is generated automatically on every appointment creation via the creating Eloquent boot hook:
// Cita model — booted()
static::creating(function (Cita $cita) {
    if (empty($cita->qr_token)) {
        $cita->qr_token     = (string) Str::uuid();
        $cita->qr_generado_en = now();
    }
});
The token is embedded in a QR code that can be scanned at the front desk. Staff members can search today’s appointments by patient name, codigo_paciente, or phone number via GET /citas-pacientes/buscar?q=. The CitaController::checkin() and CitaController::buscarParaCheckin() methods are implemented and ready — the dedicated staff-side check-in routes will be registered in a future release. Check-in updates the record:
$cita->update([
    'estatus'        => Cita::ESTATUS_CHECKIN,
    'checkin_en'     => now(),
    'checkin_metodo' => $data['metodo'], // 'qr' | 'manual'
]);

Visitor Self Check-In (Unauthenticated Kiosk)

XHealtXperience provides a fully public check-in page suitable for a tablet mounted in the waiting room. No login is required. The route is protected instead by a long random token stored in tenant_settings.visitor_checkin_token:
MethodPathDescription
GET/citas/checkin/visitante/{token}Renders the self-service check-in UI
GET/citas/checkin/visitante/{token}/buscar?q=Searches today’s appointments (returns minimal fields only)
POST/citas/checkin/visitante/{token}/citas/{cita}Confirms check-in for the matched appointment
The visitor search endpoint intentionally returns a minimal subset of fields (no phone, no email, no clinical notes) to protect patient privacy on a shared screen. If the token in the URL does not match the one stored in tenant_settings, all three endpoints return 403. An Administrador Clinica can rotate the token at any time via POST /citas/checkin/visitante/regenerar — the previous URL is immediately invalidated.

Cancellation and Penalty Window

The Cita model exposes a helper method to determine whether a cancellation falls inside the 24-hour penalty window:
// Cita::estaDentroDeVentanaPenalizable()
public function estaDentroDeVentanaPenalizable(): bool
{
    return now()->diffInHours($this->hora_inicio, false) < 24
        && now()->lessThan($this->hora_inicio);
}
The CitaController::cancelar() method is fully implemented and accepts the following payload. The dedicated cancellation route (POST /citas/{cita}/cancelar) is planned for a future release — cancellation is currently handled via the Quirófano controller for surgical appointments.
motivo
string
required
Reason for cancellation (max 1,000 characters). Always required.
aplicar_penalizacion
boolean
Whether to flag a penalty. Only takes effect when the appointment is inside the 24-hour window; ignored otherwise. The penalty amount is read from tenant_settings.monto_penalizacion_cancelacion.
alcance
string
esta (default) cancels only this occurrence. serie cancels all future active occurrences in the same recurring series.
When a penalty is applied, the fields penalizacion_aplica, penalizacion_monto, and penalizacion_cobrada are set on the record, allowing billing staff to track collection separately.

Drag-and-Drop Rescheduling

The calendar view supports drag-and-drop rescheduling via PATCH /citas/{cita}/reprogramar. The duration is preserved; only fecha and hora_inicio change. Conflict detection runs the same way as during creation. Pass forzar = true to skip conflict checking and accept the overbooking.

Route Reference

Routes require auth + two_factor + check_inactivity unless the “Required Role” column states otherwise.
MethodPathDescriptionRequired Role
GET/citasOutlook-style calendar view (day/week/month)Recepcion, Doctor, Administrador Clinica, Administrador Profesionista
GET/citas/createNew appointment formRecepcion, Doctor, Administrador Clinica, Administrador Profesionista
POST/citasCreate appointment (single or recurring)Recepcion, Doctor, Administrador Clinica, Administrador Profesionista
PATCH/citas/{cita}/reprogramarDrag-and-drop rescheduleRecepcion, Doctor, Administrador Clinica, Administrador Profesionista
POST/citas/{cita}/resolver-conflictoAccept or cancel a pending series conflictRecepcion, Doctor, Administrador Clinica, Administrador Profesionista
GET/citas-pacientes/buscarTypeahead patient search for appointment formRecepcion, Doctor, Administrador Clinica, Administrador Profesionista
GET/citas/checkin/visitante/{token}Visitor self-check-in pageNo auth required
GET/citas/checkin/visitante/{token}/buscarVisitor appointment searchNo auth required
POST/citas/checkin/visitante/{token}/citas/{cita}Visitor check-in confirmationNo auth required
POST/citas/checkin/visitante/regenerarRotate visitor check-in tokenAdministrador Clinica

Build docs developers (and LLMs) love