160 lines
4.8 KiB
PHP
160 lines
4.8 KiB
PHP
<?php
|
|
/**
|
|
* Helpers compartidos para los endpoints del módulo Registro de Exámenes.
|
|
* Incluido al inicio de cada archivo en modules/registro_exams/api/.
|
|
*/
|
|
|
|
ob_start();
|
|
|
|
require_once __DIR__ . '/../../../config/config.php';
|
|
|
|
error_reporting(E_ERROR | E_PARSE);
|
|
ini_set('display_errors', '0');
|
|
ini_set('html_errors', '0');
|
|
|
|
register_shutdown_function(function () {
|
|
$err = error_get_last();
|
|
if ($err && in_array($err['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
|
|
ob_clean();
|
|
http_response_code(500);
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
echo json_encode(['ok' => false, 'error' => 'Error interno: ' . $err['message']]);
|
|
}
|
|
});
|
|
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
header('Access-Control-Allow-Origin: *');
|
|
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
|
|
header('Access-Control-Allow-Headers: Content-Type');
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
|
http_response_code(204);
|
|
exit;
|
|
}
|
|
|
|
// ── Respuestas JSON ──────────────────────────────────────────
|
|
|
|
function jsonOk(array $payload = [], string $mensaje = ''): void
|
|
{
|
|
ob_clean();
|
|
$resp = ['ok' => true, 'success' => true];
|
|
if ($mensaje) {
|
|
$resp['message'] = $mensaje;
|
|
}
|
|
echo json_encode(array_merge($resp, $payload), JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
|
|
function jsonError(string $mensaje, int $code = 400): void
|
|
{
|
|
ob_clean();
|
|
http_response_code($code);
|
|
echo json_encode(['ok' => false, 'success' => false, 'error' => $mensaje], JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
|
|
function requireMethod(string $method): void
|
|
{
|
|
if ($_SERVER['REQUEST_METHOD'] !== strtoupper($method)) {
|
|
jsonError('Método no permitido', 405);
|
|
}
|
|
}
|
|
|
|
function inputJson(): array
|
|
{
|
|
static $parsed = null;
|
|
if ($parsed === null) {
|
|
$raw = file_get_contents('php://input');
|
|
$parsed = json_decode($raw, true) ?? [];
|
|
}
|
|
return $parsed;
|
|
}
|
|
|
|
// ── Autenticación ────────────────────────────────────────────
|
|
|
|
function requireAuth(): void
|
|
{
|
|
if (empty($_SESSION['admin_user']['id'])) {
|
|
jsonError('No autenticado', 401);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Exige sesión + acceso al módulo registro_exams.
|
|
*/
|
|
function requireExams(): void
|
|
{
|
|
requireAuth();
|
|
$modules = $_SESSION['admin_user']['modules'] ?? [];
|
|
$roleId = $_SESSION['admin_user']['role_id'] ?? null;
|
|
|
|
// Superadmin / admin global = acceso total
|
|
if (empty($roleId)) {
|
|
return;
|
|
}
|
|
|
|
if (!in_array('registro_exams', (array) $modules, true)) {
|
|
jsonError('Sin acceso al módulo Registro Exámenes', 403);
|
|
}
|
|
}
|
|
|
|
function adminId(): ?int
|
|
{
|
|
return isset($_SESSION['admin_user']['id'])
|
|
? (int) $_SESSION['admin_user']['id']
|
|
: null;
|
|
}
|
|
|
|
// ── Helper BD ────────────────────────────────────────────────
|
|
|
|
function db(): PDO
|
|
{
|
|
return Database::getInstance()->getConnection();
|
|
}
|
|
|
|
// ── Código de orden ──────────────────────────────────────────
|
|
|
|
/**
|
|
* Genera el siguiente código de orden legible: EX-YYYYMMDD-NNNN
|
|
* Usa SELECT FOR UPDATE para evitar duplicados en concurrencia.
|
|
*/
|
|
function generarCodigoOrden(): string
|
|
{
|
|
$pdo = db();
|
|
$hoy = date('Ymd');
|
|
$prefijo = "EX-{$hoy}-";
|
|
|
|
$stmt = $pdo->prepare(
|
|
"SELECT COALESCE(MAX(CAST(SUBSTRING(codigo, -4) AS UNSIGNED)), 0) + 1 AS siguiente
|
|
FROM exam_ordenes
|
|
WHERE codigo LIKE ?
|
|
FOR UPDATE"
|
|
);
|
|
$stmt->execute(["{$prefijo}%"]);
|
|
$n = (int) $stmt->fetchColumn();
|
|
|
|
return $prefijo . str_pad($n, 4, '0', STR_PAD_LEFT);
|
|
}
|
|
|
|
// ── Etiqueta de prioridad ────────────────────────────────────
|
|
|
|
function badgePrioridad(string $p): string
|
|
{
|
|
return match ($p) {
|
|
'urgente' => '<span class="badge bg-warning text-dark">Urgente</span>',
|
|
'stat' => '<span class="badge bg-danger">STAT</span>',
|
|
default => '<span class="badge bg-secondary">Normal</span>',
|
|
};
|
|
}
|
|
|
|
function badgeEstadoOrden(string $e): string
|
|
{
|
|
return match ($e) {
|
|
'en_proceso' => '<span class="badge bg-primary">En proceso</span>',
|
|
'completa' => '<span class="badge bg-success">Completa</span>',
|
|
'entregada' => '<span class="badge bg-info text-dark">Entregada</span>',
|
|
'cancelada' => '<span class="badge bg-dark">Cancelada</span>',
|
|
default => '<span class="badge bg-light text-dark border">Pendiente</span>',
|
|
};
|
|
}
|