diff --git a/dashboard_principal.php b/dashboard_principal.php
index d5770b29..973e39ac 100644
--- a/dashboard_principal.php
+++ b/dashboard_principal.php
@@ -13,15 +13,23 @@ if (!isset($_SESSION['user_id']) || !in_array($_SESSION['user_role'], ['Administ
$db = db();
// --- LÓGICA DE FILTROS ---
-$period = $_GET['period'] ?? '7';
-$start_date = $_GET['start_date'] ?? '';
-$end_date = $_GET['end_date'] ?? '';
+$period = $_GET['period'] ?? 'today';
+$start_date = trim((string)($_GET['start_date'] ?? ''));
+$end_date = trim((string)($_GET['end_date'] ?? ''));
+$isValidDashboardDate = static function (string $date): bool {
+ $parsed = DateTimeImmutable::createFromFormat('Y-m-d', $date);
+ return $parsed instanceof DateTimeImmutable && $parsed->format('Y-m-d') === $date;
+};
$date_condition = "";
$label_period = "";
-if ($period === 'custom' && !empty($start_date) && !empty($end_date)) {
- $date_condition = "DATE(p.created_at) BETWEEN '$start_date' AND '$end_date'";
+if ($period === 'custom' && $isValidDashboardDate($start_date) && $isValidDashboardDate($end_date)) {
+ if ($start_date > $end_date) {
+ [$start_date, $end_date] = [$end_date, $start_date];
+ }
+
+ $date_condition = "DATE(p.created_at) BETWEEN " . $db->quote($start_date) . " AND " . $db->quote($end_date);
$label_period = "Desde " . date('d/m/Y', strtotime($start_date)) . " hasta " . date('d/m/Y', strtotime($end_date));
} else {
switch ($period) {
@@ -66,9 +74,9 @@ if ($period === 'custom' && !empty($start_date) && !empty($end_date)) {
$label_period = "Último Año";
break;
default:
- $date_condition = "DATE(p.created_at) >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)";
- $label_period = "Últimos 7 días";
- $period = '7';
+ $date_condition = "DATE(p.created_at) = CURDATE()";
+ $label_period = "Hoy";
+ $period = 'today';
}
}
diff --git a/db/migrations/077_add_pendiente_to_tipo_paquete_enum.sql b/db/migrations/077_add_pendiente_to_tipo_paquete_enum.sql
new file mode 100644
index 00000000..5e9038ea
--- /dev/null
+++ b/db/migrations/077_add_pendiente_to_tipo_paquete_enum.sql
@@ -0,0 +1,3 @@
+-- Deprecated/no-op.
+-- PENDIENTE belongs to pedidos.estado, not to pedidos.tipo_paquete.
+-- The final package ENUM is enforced in 078_clean_tipo_paquete_enum.sql.
diff --git a/db/migrations/078_clean_tipo_paquete_enum.sql b/db/migrations/078_clean_tipo_paquete_enum.sql
new file mode 100644
index 00000000..5e8c6851
--- /dev/null
+++ b/db/migrations/078_clean_tipo_paquete_enum.sql
@@ -0,0 +1,4 @@
+-- Keep pedidos.tipo_paquete aligned with the package selector.
+-- PENDIENTE belongs to pedidos.estado, not to tipo_paquete.
+UPDATE pedidos SET tipo_paquete = NULL WHERE tipo_paquete = 'PENDIENTE';
+ALTER TABLE pedidos MODIFY COLUMN tipo_paquete ENUM('RUTA', 'CONTRAENTREGA', 'NO CONTESTA, VOLVER A LLAMAR', 'PENDIENTE A RETORNO', 'COMPLETADO', 'EMPAQUETADO', 'RETORNADO', 'ANULADO') DEFAULT NULL;
diff --git a/db/migrations/079_rename_empaquetado_to_preparado_tipo_paquete.sql b/db/migrations/079_rename_empaquetado_to_preparado_tipo_paquete.sql
new file mode 100644
index 00000000..fea972ed
--- /dev/null
+++ b/db/migrations/079_rename_empaquetado_to_preparado_tipo_paquete.sql
@@ -0,0 +1,5 @@
+-- Rename package status EMPAQUETADO to PREPARADO in pedidos.tipo_paquete.
+-- EMPAQUETADO is kept only as a temporary legacy value during migration so existing rows can be converted safely.
+ALTER TABLE pedidos MODIFY COLUMN tipo_paquete ENUM('RUTA', 'CONTRAENTREGA', 'NO CONTESTA, VOLVER A LLAMAR', 'PENDIENTE A RETORNO', 'COMPLETADO', 'EMPAQUETADO', 'PREPARADO', 'RETORNADO', 'ANULADO') DEFAULT NULL;
+UPDATE pedidos SET tipo_paquete = 'PREPARADO' WHERE tipo_paquete = 'EMPAQUETADO';
+ALTER TABLE pedidos MODIFY COLUMN tipo_paquete ENUM('RUTA', 'CONTRAENTREGA', 'NO CONTESTA, VOLVER A LLAMAR', 'PENDIENTE A RETORNO', 'COMPLETADO', 'PREPARADO', 'RETORNADO', 'ANULADO') DEFAULT NULL;
diff --git a/includes/tipo_paquete.php b/includes/tipo_paquete.php
new file mode 100644
index 00000000..1a6d4960
--- /dev/null
+++ b/includes/tipo_paquete.php
@@ -0,0 +1,136 @@
+ 'RUTA',
+ 'CONTRAENTREGA' => 'CONTRAENTREGA',
+ 'NOCONTESTAVOLVERALLAMAR' => 'NO CONTESTA, VOLVER A LLAMAR',
+ 'PENDIENTEARETORNO' => 'PENDIENTE A RETORNO',
+ 'COMPLETADO' => 'COMPLETADO',
+ 'EMPAQUETADO' => 'PREPARADO',
+ 'PREPARADO' => 'PREPARADO',
+ 'EMPACADO' => 'PREPARADO',
+ 'EMPAQUETARDO' => 'PREPARADO',
+ 'RETORNADO' => 'RETORNADO',
+ 'ANULADO' => 'ANULADO',
+ ];
+
+ return $aliases[$canonicalKey] ?? $normalized;
+ }
+}
+
+if (!function_exists('tipoPaqueteIsValid')) {
+ function tipoPaqueteIsValid(?string $value): bool
+ {
+ return $value === null || in_array($value, tipoPaqueteValidValues(), true);
+ }
+}
+
+if (!function_exists('ensureTipoPaqueteEnumDefinition')) {
+ function ensureTipoPaqueteEnumDefinition(PDO $pdo, bool $force = false): void
+ {
+ static $checked = false;
+
+ if ($checked && !$force) {
+ return;
+ }
+
+ $stmt = $pdo->query("SHOW COLUMNS FROM pedidos LIKE 'tipo_paquete'");
+ $column = $stmt ? $stmt->fetch(PDO::FETCH_ASSOC) : false;
+ if (!$column || empty($column['Type'])) {
+ $checked = true;
+ return;
+ }
+
+ $currentType = (string) $column['Type'];
+ $validValues = tipoPaqueteValidValues();
+ $missingRequiredValue = false;
+
+ foreach ($validValues as $value) {
+ if (strpos($currentType, "'" . str_replace("'", "''", $value) . "'") === false) {
+ $missingRequiredValue = true;
+ break;
+ }
+ }
+
+ $hasLegacyPendiente = strpos($currentType, "'PENDIENTE'") !== false;
+ $hasLegacyEmpaquetado = strpos($currentType, "'EMPAQUETADO'") !== false;
+
+ if ($force || $missingRequiredValue || $hasLegacyPendiente || $hasLegacyEmpaquetado) {
+ $quotedValues = array_map(static function (string $value) use ($pdo): string {
+ return $pdo->quote($value);
+ }, $validValues);
+ $enumValuesSql = implode(', ', $quotedValues);
+
+ $transitionValues = array_values(array_unique(array_merge(
+ $validValues,
+ ['EMPAQUETADO', 'PENDIENTE', 'NO CONTESTA', 'VOLVER A LLAMAR']
+ )));
+ $quotedTransitionValues = array_map(static function (string $value) use ($pdo): string {
+ return $pdo->quote($value);
+ }, $transitionValues);
+ $transitionValuesSql = implode(', ', $quotedTransitionValues);
+
+ $pdo->exec("ALTER TABLE pedidos MODIFY COLUMN tipo_paquete ENUM($transitionValuesSql) DEFAULT NULL");
+ $pdo->exec("UPDATE pedidos SET tipo_paquete = 'PREPARADO' WHERE tipo_paquete = 'EMPAQUETADO'");
+ $pdo->exec("UPDATE pedidos SET tipo_paquete = 'NO CONTESTA, VOLVER A LLAMAR' WHERE tipo_paquete IN ('NO CONTESTA', 'VOLVER A LLAMAR')");
+ $pdo->exec("UPDATE pedidos SET tipo_paquete = NULL WHERE tipo_paquete = 'PENDIENTE'");
+ $pdo->exec("UPDATE pedidos SET tipo_paquete = NULL WHERE tipo_paquete IS NOT NULL AND tipo_paquete NOT IN ($enumValuesSql)");
+ $pdo->exec("ALTER TABLE pedidos MODIFY COLUMN tipo_paquete ENUM($enumValuesSql) DEFAULT NULL");
+ }
+
+ $checked = true;
+ }
+}
diff --git a/pedidos_contraentrega.php b/pedidos_contraentrega.php
index db3744cc..e197a67b 100644
--- a/pedidos_contraentrega.php
+++ b/pedidos_contraentrega.php
@@ -7,7 +7,9 @@ if (!isset($_SESSION['user_id'])) {
require_once 'db/config.php';
require_once 'includes/contraentrega_cobertura.php';
+require_once 'includes/tipo_paquete.php';
$pdo = db();
+ensureTipoPaqueteEnumDefinition($pdo);
$user_id = $_SESSION['user_id'];
$user_role = $_SESSION['user_role'] ?? 'Asesor';
@@ -320,7 +322,7 @@ include 'layout_header.php';
" . htmlspecialchars($paquete) . "";
@@ -333,7 +335,7 @@ include 'layout_header.php';